| // 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'; |
| import '../globals.dart' as globals; |
| import '../project.dart'; |
| |
| String flutterMacOSFrameworkDir(BuildMode mode, FileSystem fileSystem, |
| Artifacts artifacts) { |
| final String flutterMacOSFramework = artifacts.getArtifactPath( |
| Artifact.flutterMacOSFramework, |
| platform: TargetPlatform.darwin, |
| 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({ |
| required FlutterProject project, |
| required BuildInfo buildInfo, |
| String? targetOverride, |
| bool useMacOSConfig = false, |
| String? buildDirOverride, |
| }) async { |
| final List<String> xcodeBuildSettings = await _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({ |
| required FlutterProject project, |
| required List<String> xcodeBuildSettings, |
| 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({ |
| required FlutterProject project, |
| required List<String> xcodeBuildSettings, |
| 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. |
| String? parsedBuildName({ |
| required FlutterManifest manifest, |
| BuildInfo? buildInfo, |
| }) { |
| final String? buildNameToParse = buildInfo?.buildName ?? manifest.buildName; |
| return validatedBuildNameForPlatform(TargetPlatform.ios, buildNameToParse, globals.logger); |
| } |
| |
| /// Build number parsed and validated from build info and manifest. Used for CFBundleVersion. |
| String? parsedBuildNumber({ |
| required FlutterManifest manifest, |
| BuildInfo? buildInfo, |
| }) { |
| String? buildNumberToParse = buildInfo?.buildNumber ?? manifest.buildNumber; |
| final String? buildNumber = validatedBuildNumberForPlatform( |
| 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' |
| Future<List<String>> _xcodeBuildSettingsLines({ |
| required FlutterProject project, |
| required BuildInfo buildInfo, |
| String? targetOverride, |
| bool useMacOSConfig = false, |
| String? buildDirOverride, |
| }) async { |
| final List<String> xcodeBuildSettings = <String>[]; |
| |
| final String flutterRoot = globals.fs.path.normalize(Cache.flutterRoot!); |
| 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'); |
| |
| final Artifacts? artifacts = globals.artifacts; |
| if (artifacts is LocalEngineArtifacts) { |
| final LocalEngineArtifacts localEngineArtifacts = artifacts; |
| 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('_arm64')) { |
| arch = 'arm64'; |
| } else if (localEngineName.contains('_sim')) { |
| 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 { |
| String excludedSimulatorArchs = 'i386'; |
| |
| // If any plugins or their dependencies do not support arm64 simulators |
| // (to run natively without Rosetta translation on an ARM Mac), |
| // the app will fail to build unless it also excludes arm64 simulators. |
| if (!(await project.ios.pluginsSupportArmSimulator())) { |
| excludedSimulatorArchs += ' arm64'; |
| } |
| xcodeBuildSettings.add('EXCLUDED_ARCHS[sdk=iphonesimulator*]=$excludedSimulatorArchs'); |
| } |
| |
| for (final MapEntry<String, String> config in buildInfo.toEnvironmentConfig().entries) { |
| xcodeBuildSettings.add('${config.key}=${config.value}'); |
| } |
| return xcodeBuildSettings; |
| } |