xcode_build_settings.dart 8.05 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../artifacts.dart';
import '../base/file_system.dart';
import '../build_info.dart';
import '../cache.dart';
import '../flutter_manifest.dart';
10
import '../globals_null_migrated.dart' as globals;
11 12 13 14 15 16
import '../project.dart';

String flutterMacOSFrameworkDir(BuildMode mode, FileSystem fileSystem,
    Artifacts artifacts) {
  final String flutterMacOSFramework = artifacts.getArtifactPath(
    Artifact.flutterMacOSFramework,
17
    platform: TargetPlatform.darwin,
18 19 20 21 22 23 24 25 26 27 28 29 30 31
    mode: mode,
  );
  return fileSystem.path
      .normalize(fileSystem.path.dirname(flutterMacOSFramework));
}

/// Writes or rewrites Xcode property files with the specified information.
///
/// useMacOSConfig: Optional parameter that controls whether we use the macOS
/// project file instead. Defaults to false.
///
/// targetOverride: Optional parameter, if null or unspecified the default value
/// from xcode_backend.sh is used 'lib/main.dart'.
Future<void> updateGeneratedXcodeProperties({
32 33 34
  required FlutterProject project,
  required BuildInfo buildInfo,
  String? targetOverride,
35
  bool useMacOSConfig = false,
36
  String? buildDirOverride,
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
}) async {
  final List<String> xcodeBuildSettings = _xcodeBuildSettingsLines(
    project: project,
    buildInfo: buildInfo,
    targetOverride: targetOverride,
    useMacOSConfig: useMacOSConfig,
    buildDirOverride: buildDirOverride,
  );

  _updateGeneratedXcodePropertiesFile(
    project: project,
    xcodeBuildSettings: xcodeBuildSettings,
    useMacOSConfig: useMacOSConfig,
  );

  _updateGeneratedEnvironmentVariablesScript(
    project: project,
    xcodeBuildSettings: xcodeBuildSettings,
    useMacOSConfig: useMacOSConfig,
  );
}

/// Generate a xcconfig file to inherit FLUTTER_ build settings
/// for Xcode targets that need them.
/// See [XcodeBasedProject.generatedXcodePropertiesFile].
void _updateGeneratedXcodePropertiesFile({
63 64
  required FlutterProject project,
  required List<String> xcodeBuildSettings,
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  bool useMacOSConfig = false,
}) {
  final StringBuffer localsBuffer = StringBuffer();

  localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
  xcodeBuildSettings.forEach(localsBuffer.writeln);
  final File generatedXcodePropertiesFile = useMacOSConfig
    ? project.macos.generatedXcodePropertiesFile
    : project.ios.generatedXcodePropertiesFile;

  generatedXcodePropertiesFile.createSync(recursive: true);
  generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
}

/// Generate a script to export all the FLUTTER_ environment variables needed
/// as flags for Flutter tools.
/// See [XcodeBasedProject.generatedEnvironmentVariableExportScript].
void _updateGeneratedEnvironmentVariablesScript({
83 84
  required FlutterProject project,
  required List<String> xcodeBuildSettings,
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
  bool useMacOSConfig = false,
}) {
  final StringBuffer localsBuffer = StringBuffer();

  localsBuffer.writeln('#!/bin/sh');
  localsBuffer.writeln('# This is a generated file; do not edit or check into version control.');
  for (final String line in xcodeBuildSettings) {
    if (!line.contains('[')) { // Exported conditional Xcode build settings do not work.
      localsBuffer.writeln('export "$line"');
    }
  }

  final File generatedModuleBuildPhaseScript = useMacOSConfig
    ? project.macos.generatedEnvironmentVariableExportScript
    : project.ios.generatedEnvironmentVariableExportScript;
  generatedModuleBuildPhaseScript.createSync(recursive: true);
  generatedModuleBuildPhaseScript.writeAsStringSync(localsBuffer.toString());
  globals.os.chmod(generatedModuleBuildPhaseScript, '755');
}

/// Build name parsed and validated from build info and manifest. Used for CFBundleShortVersionString.
106 107 108
String? parsedBuildName({
  required FlutterManifest manifest,
  BuildInfo? buildInfo,
109
}) {
110
  final String? buildNameToParse = buildInfo?.buildName ?? manifest.buildName;
111 112 113 114
  return validatedBuildNameForPlatform(TargetPlatform.ios, buildNameToParse, globals.logger);
}

/// Build number parsed and validated from build info and manifest. Used for CFBundleVersion.
115 116 117
String? parsedBuildNumber({
  required FlutterManifest manifest,
  BuildInfo? buildInfo,
118
}) {
119 120
  String? buildNumberToParse = buildInfo?.buildNumber ?? manifest.buildNumber;
  final String? buildNumber = validatedBuildNumberForPlatform(
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    TargetPlatform.ios,
    buildNumberToParse,
    globals.logger,
  );
  if (buildNumber != null && buildNumber.isNotEmpty) {
    return buildNumber;
  }
  // Drop back to parsing build name if build number is not present. Build number is optional in the manifest, but
  // FLUTTER_BUILD_NUMBER is required as the backing value for the required CFBundleVersion.
  buildNumberToParse = buildInfo?.buildName ?? manifest.buildName;
  return validatedBuildNumberForPlatform(
    TargetPlatform.ios,
    buildNumberToParse,
    globals.logger,
  );
}

/// List of lines of build settings. Example: 'FLUTTER_BUILD_DIR=build'
List<String> _xcodeBuildSettingsLines({
140 141 142
  required FlutterProject project,
  required BuildInfo buildInfo,
  String? targetOverride,
143
  bool useMacOSConfig = false,
144
  String? buildDirOverride,
145 146 147
}) {
  final List<String> xcodeBuildSettings = <String>[];

148
  final String flutterRoot = globals.fs.path.normalize(Cache.flutterRoot!);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  xcodeBuildSettings.add('FLUTTER_ROOT=$flutterRoot');

  // This holds because requiresProjectRoot is true for this command
  xcodeBuildSettings.add('FLUTTER_APPLICATION_PATH=${globals.fs.path.normalize(project.directory.path)}');

  // Tell CocoaPods behavior to codesign in parallel with rest of scripts to speed it up.
  // Value must be "true", not "YES". https://github.com/CocoaPods/CocoaPods/pull/6088
  xcodeBuildSettings.add('COCOAPODS_PARALLEL_CODE_SIGN=true');

  // Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current].
  if (targetOverride != null) {
    xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride');
  }

  // The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
  xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${buildDirOverride ?? getBuildDirectory()}');

  final String buildName = parsedBuildName(manifest: project.manifest, buildInfo: buildInfo) ?? '1.0.0';
  xcodeBuildSettings.add('FLUTTER_BUILD_NAME=$buildName');

  final String buildNumber = parsedBuildNumber(manifest: project.manifest, buildInfo: buildInfo) ?? '1';
  xcodeBuildSettings.add('FLUTTER_BUILD_NUMBER=$buildNumber');

172
  final Artifacts? artifacts = globals.artifacts;
173 174
  if (artifacts is LocalEngineArtifacts) {
    final LocalEngineArtifacts localEngineArtifacts = artifacts;
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    final String engineOutPath = localEngineArtifacts.engineOutPath;
    xcodeBuildSettings.add('FLUTTER_ENGINE=${globals.fs.path.dirname(globals.fs.path.dirname(engineOutPath))}');

    final String localEngineName = globals.fs.path.basename(engineOutPath);
    xcodeBuildSettings.add('LOCAL_ENGINE=$localEngineName');

    // Tell Xcode not to build universal binaries for local engines, which are
    // single-architecture.
    //
    // NOTE: this assumes that local engine binary paths are consistent with
    // the conventions uses in the engine: 32-bit iOS engines are built to
    // paths ending in _arm, 64-bit builds are not.
    //
    // Skip this step for macOS builds.
    if (!useMacOSConfig) {
      String arch;
      if (localEngineName.endsWith('_arm')) {
        arch = 'armv7';
      } else if (localEngineName.contains('_sim')) {
        // Apple Silicon ARM simulators not yet supported.
        arch = 'x86_64';
      } else {
        arch = 'arm64';
      }
      xcodeBuildSettings.add('ARCHS=$arch');
    }
  }
  if (useMacOSConfig) {
    // ARM not yet supported https://github.com/flutter/flutter/issues/69221
    xcodeBuildSettings.add('EXCLUDED_ARCHS=arm64');
  } else {
    // Apple Silicon ARM simulators not yet supported.
    xcodeBuildSettings.add('EXCLUDED_ARCHS[sdk=iphonesimulator*]=arm64 i386');
  }

  for (final MapEntry<String, String> config in buildInfo.toEnvironmentConfig().entries) {
    xcodeBuildSettings.add('${config.key}=${config.value}');
  }
  return xcodeBuildSettings;
}