From e21d82207413ad50b25eb6302b7a93e7172b35e7 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 6 Jan 2021 11:14:05 -0800 Subject: [PATCH] Remove build ios-framework --universal flag (#73383) --- .../build_ios_framework_module_test.dart | 167 ++++++------------ .../lib/src/commands/build_ios_framework.dart | 98 +--------- 2 files changed, 63 insertions(+), 202 deletions(-) diff --git a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart index 82266bc6603..bdfa1b5be0e 100644 --- a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart +++ b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart @@ -10,7 +10,7 @@ import 'package:flutter_devicelab/framework/task_result.dart'; import 'package:flutter_devicelab/framework/utils.dart'; import 'package:path/path.dart' as path; -/// Tests that iOS .frameworks can be built on module projects. +/// Tests that iOS .xcframeworks can be built. Future main() async { await task(() async { @@ -76,10 +76,10 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals ); }); - // First, build the module in Debug to copy the debug version of Flutter.framework. - // This proves "flutter build ios-framework" re-copies the relevant Flutter.framework, + // First, build the module in Debug to copy the debug version of Flutter.xcframework. + // This proves "flutter build ios-framework" re-copies the relevant Flutter.xcframework, // otherwise building plugins with bitcode will fail linking because the debug version - // of Flutter.framework does not contain bitcode. + // of Flutter.xcframework does not contain bitcode. await inDirectory(projectDir, () async { await flutter( 'build', @@ -101,7 +101,6 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals 'build', options: [ 'ios-framework', - '--universal', '--verbose', '--output=$outputDirectoryName' ], @@ -110,40 +109,6 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals final String outputPath = path.join(projectDir.path, outputDirectoryName); - section('Check debug build has Dart snapshot as asset'); - - checkFileExists(path.join( - outputPath, - 'Debug', - 'App.framework', - 'flutter_assets', - 'vm_snapshot_data', - )); - - section('Check debug build has no Dart AOT'); - - // There's still an App.framework with a dylib, but it's empty. - checkFileExists(path.join( - outputPath, - 'Debug', - 'App.framework', - 'App', - )); - - final String debugAppFrameworkPath = path.join( - outputPath, - 'Debug', - 'App.framework', - 'App', - ); - final String aotSymbols = await dylibSymbols(debugAppFrameworkPath); - - if (aotSymbols.contains('architecture') || - aotSymbols.contains('_kDartVmSnapshot')) { - throw TaskResult.failure('Debug App.framework contains AOT'); - } - await _checkFrameworkArchs(debugAppFrameworkPath, true); - // Xcode changed the name of this generated directory in Xcode 12. const String xcode11ArmDirectoryName = 'ios-armv7_arm64'; const String xcode12ArmDirectoryName = 'ios-arm64_armv7'; @@ -202,26 +167,49 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals throw const FileSystemException('Expected Flutter.framework binary to exist.'); } + final String debugAppFrameworkPath = path.join( + outputPath, + 'Debug', + 'App.xcframework', + localXcodeArmDirectoryName, + 'App.framework', + 'App', + ); + checkFileExists(debugAppFrameworkPath); + + section('Check debug build has Dart snapshot as asset'); + checkFileExists(path.join( outputPath, 'Debug', 'App.xcframework', 'ios-x86_64-simulator', 'App.framework', - 'App', + 'flutter_assets', + 'vm_snapshot_data', )); + section('Check debug build has no Dart AOT'); + + final String aotSymbols = await dylibSymbols(debugAppFrameworkPath); + + if (aotSymbols.contains('architecture') || + aotSymbols.contains('_kDartVmSnapshot')) { + throw TaskResult.failure('Debug App.framework contains AOT'); + } + section('Check profile, release builds has Dart AOT dylib'); for (final String mode in ['Profile', 'Release']) { final String appFrameworkPath = path.join( outputPath, mode, + 'App.xcframework', + localXcodeArmDirectoryName, 'App.framework', 'App', ); - await _checkFrameworkArchs(appFrameworkPath, false); await _checkBitcode(appFrameworkPath, mode); final String aotSymbols = await dylibSymbols(appFrameworkPath); @@ -231,20 +219,13 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals } checkFileNotExists(path.join( - outputPath, - mode, - 'App.framework', - 'flutter_assets', - 'vm_snapshot_data', - )); - - checkFileExists(path.join( outputPath, mode, 'App.xcframework', localXcodeArmDirectoryName, 'App.framework', - 'App', + 'flutter_assets', + 'vm_snapshot_data', )); checkFileNotExists(path.join( @@ -261,23 +242,16 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals for (final String mode in ['Debug', 'Profile', 'Release']) { final String engineFrameworkPath = path.join( - outputPath, - mode, - 'Flutter.framework', - 'Flutter', - ); - - await _checkFrameworkArchs(engineFrameworkPath, true); - await _checkBitcode(engineFrameworkPath, mode); - - checkFileExists(path.join( outputPath, mode, 'Flutter.xcframework', builderXcodeArmDirectoryName, 'Flutter.framework', 'Flutter', - )); + ); + + await _checkBitcode(engineFrameworkPath, mode); + checkFileExists(path.join( outputPath, mode, @@ -286,34 +260,30 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals 'Flutter.framework', 'Flutter', )); - } - section("Check all modes' engine header"); - - for (final String mode in ['Debug', 'Profile', 'Release']) { - checkFileExists(path.join(outputPath, mode, 'Flutter.framework', 'Headers', 'Flutter.h')); + checkFileExists(path.join( + outputPath, + mode, + 'Flutter.xcframework', + 'ios-x86_64-simulator', + 'Flutter.framework', + 'Headers', + 'Flutter.h', + )); } section('Check all modes have plugins'); for (final String mode in ['Debug', 'Profile', 'Release']) { final String pluginFrameworkPath = path.join( - outputPath, - mode, - 'device_info.framework', - 'device_info', - ); - await _checkFrameworkArchs(pluginFrameworkPath, mode == 'Debug'); - await _checkBitcode(pluginFrameworkPath, mode); - - checkFileExists(path.join( outputPath, mode, 'device_info.xcframework', localXcodeArmDirectoryName, 'device_info.framework', 'device_info', - )); + ); + await _checkBitcode(pluginFrameworkPath, mode); checkFileExists(path.join( outputPath, @@ -362,20 +332,13 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals final String registrantFrameworkPath = path.join( outputPath, mode, + 'FlutterPluginRegistrant.xcframework', + localXcodeArmDirectoryName, 'FlutterPluginRegistrant.framework', - 'FlutterPluginRegistrant' + 'FlutterPluginRegistrant', ); - - await _checkFrameworkArchs(registrantFrameworkPath, mode == 'Debug'); await _checkBitcode(registrantFrameworkPath, mode); - checkFileExists(path.join( - outputPath, - mode, - 'FlutterPluginRegistrant.framework', - 'Headers', - 'GeneratedPluginRegistrant.h', - )); checkFileExists(path.join( outputPath, mode, @@ -412,7 +375,6 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals options: [ 'ios-framework', '--cocoapods', - '--universal', '--force', // Allow podspec creation on master. '--output=$cocoapodsOutputDirectoryName' ], @@ -430,29 +392,29 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals checkDirectoryExists(path.join( cocoapodsOutputPath, mode, - 'App.framework', + 'App.xcframework', )); if (Directory(path.join( cocoapodsOutputPath, mode, - 'FlutterPluginRegistrant.framework', + 'FlutterPluginRegistrant.xcframework', )).existsSync() != isModule) { throw TaskResult.failure( - 'Unexpected FlutterPluginRegistrant.framework.'); + 'Unexpected FlutterPluginRegistrant.xcframework.'); } checkDirectoryExists(path.join( cocoapodsOutputPath, mode, - 'device_info.framework', + 'device_info.xcframework', )); checkDirectoryExists(path.join( cocoapodsOutputPath, mode, - 'package_info.framework', + 'package_info.xcframework', )); } @@ -473,27 +435,6 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals } } -Future _checkFrameworkArchs(String frameworkPath, bool shouldContainSimulator) async { - checkFileExists(frameworkPath); - - final String archs = await fileType(frameworkPath); - if (!archs.contains('armv7')) { - throw TaskResult.failure('$frameworkPath armv7 architecture missing'); - } - - if (!archs.contains('arm64')) { - throw TaskResult.failure('$frameworkPath arm64 architecture missing'); - } - final bool containsSimulator = archs.contains('x86_64'); - - // Debug should contain the simulator archs. - // Release and Profile should not. - if (containsSimulator != shouldContainSimulator) { - throw TaskResult.failure( - '$frameworkPath x86_64 architecture ${shouldContainSimulator ? 'missing' : 'present'}'); - } -} - Future _checkBitcode(String frameworkPath, String mode) async { checkFileExists(frameworkPath); diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart index 8e580a3dc14..2d971b6a2fc 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart @@ -73,14 +73,15 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { 'By default, all build configurations are built.' ) ..addFlag('universal', - help: '(Deprecated) Produce universal frameworks that include all valid architectures. ' - 'This option will be removed in a future version of Flutter.', + help: '(Deprecated) Produce universal frameworks that include all valid architectures.', negatable: true, hide: true, ) ..addFlag('xcframework', help: 'Produce xcframeworks that include all valid architectures.', + negatable: false, defaultsTo: true, + hide: true, ) ..addFlag('cocoapods', help: 'Produce a Flutter.podspec instead of an engine Flutter.xcframework (recommended if host app uses CocoaPods).', @@ -115,7 +116,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { final String name = 'ios-framework'; @override - final String description = 'Produces .frameworks for a Flutter project ' + final String description = 'Produces .xcframeworks for a Flutter project ' 'and its plugins for integration into existing, plain Xcode projects.\n' 'This can only be run on macOS hosts.'; @@ -150,16 +151,8 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { throwToolExit('Building frameworks for iOS is only supported on the Mac.'); } - if (!boolArg('universal') && !boolArg('xcframework')) { - throwToolExit('--xcframework or --universal is required.'); - } - if (boolArg('xcframework') && globals.xcode.majorVersion < 11) { - throwToolExit('--xcframework requires Xcode 11.'); - } if (boolArg('universal')) { - globals.printError('--universal has been deprecated to support Apple ' - 'Silicon ARM simulators and will be removed in a future version of ' - 'Flutter. Use --xcframework instead.'); + throwToolExit('--universal has been deprecated, only XCFrameworks are supported.'); } if ((await buildInfos).isEmpty) { throwToolExit('At least one of "--debug" or "--profile", or "--release" is required.'); @@ -196,7 +189,7 @@ class BuildIOSFrameworkCommand extends BuildSubCommand { _flutterVersion ??= globals.flutterVersion; produceFlutterPodspec(buildInfo.mode, modeDirectory, force: boolArg('force')); } else { - // Copy Flutter.framework. + // Copy Flutter.xcframework. await _produceFlutterFramework(buildInfo, modeDirectory); } @@ -311,7 +304,7 @@ end Directory modeDirectory, ) async { final Status status = globals.logger.startProgress( - ' ├─Populating Flutter.xcframework...', + ' ├─Copying Flutter.xcframework...', ); final String engineCacheFlutterFrameworkDirectory = globals.artifacts.getArtifactPath( Artifact.flutterXcframework, @@ -334,8 +327,6 @@ end } finally { status.stop(); } - - await _produceUniversalFromXCFramework(buildInfo, flutterFrameworkCopy); } Future _produceAppFramework( @@ -347,7 +338,7 @@ end const String appFrameworkName = 'App.framework'; final Status status = globals.logger.startProgress( - ' ├─Building App.framework...', + ' ├─Building App.xcframework...', ); final List environmentTypes = [ EnvironmentType.physical, @@ -407,14 +398,13 @@ end in result.exceptions.values) { globals.printError(measurement.exception.toString()); } - throwToolExit('The App.framework build failed.'); + throwToolExit('The App.xcframework build failed.'); } } } finally { status.stop(); } - await _produceUniversalFramework(frameworks, 'App', outputDirectory); await _produceXCFramework(frameworks, 'App', outputDirectory); } @@ -520,7 +510,6 @@ end .childDirectory(podFrameworkName) ]; - await _produceUniversalFramework(frameworks, binaryName, modeDirectory); await _produceXCFramework(frameworks, binaryName, modeDirectory); } } @@ -529,37 +518,6 @@ end } } - Future _produceUniversalFromXCFramework(BuildInfo buildInfo, Directory xcframework) async { - if (boolArg('universal')) { - final String frameworkBinaryName = - globals.fs.path.basenameWithoutExtension(xcframework.basename); - - final Status status = globals.logger.startProgress( - ' ├─Creating $frameworkBinaryName.framework...', - ); - try { - final Iterable frameworks = xcframework - .listSync() - .whereType() - .map((Directory triple) => triple - .listSync() - .whereType() - .firstWhere((Directory frameworkDirectory) => - frameworkDirectory.basename == - '$frameworkBinaryName.framework')); - - await _produceUniversalFramework( - frameworks, frameworkBinaryName, xcframework.parent); - } finally { - status.stop(); - } - } - - if (!boolArg('xcframework')) { - xcframework.deleteSync(recursive: true); - } - } - Future _produceXCFramework(Iterable frameworks, String frameworkBinaryName, Directory outputDirectory) async { if (!boolArg('xcframework')) { @@ -587,42 +545,4 @@ end 'Unable to create $frameworkBinaryName.xcframework: ${xcframeworkResult.stderr}'); } } - - Future _produceUniversalFramework(Iterable frameworks, - String frameworkBinaryName, Directory outputDirectory) async { - if (!boolArg('universal')) { - return; - } - final Directory outputFrameworkDirectory = - outputDirectory.childDirectory('$frameworkBinaryName.framework'); - - // Copy the first framework over completely to get headers, resources, etc. - globals.fsUtils.copyDirectorySync( - frameworks.first, - outputFrameworkDirectory, - ); - - // Recreate the framework binary by lipo'ing the framework binaries together. - final List lipoCommand = [ - ...globals.xcode.xcrunCommand(), - 'lipo', - '-create', - for (Directory framework in frameworks) ...[ - framework.childFile(frameworkBinaryName).path - ], - '-output', - outputFrameworkDirectory.childFile(frameworkBinaryName).path - ]; - - final RunResult lipoResult = await globals.processUtils.run( - lipoCommand, - workingDirectory: outputDirectory.path, - allowReentrantFlutter: false, - ); - - if (lipoResult.exitCode != 0) { - throwToolExit( - 'Unable to create $frameworkBinaryName.framework: ${lipoResult.stderr}'); - } - } }