Remove build ios-framework --universal flag (#73383)

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 82266bc..bdfa1b5 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/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<void> main() async {
   await task(() async {
 
@@ -76,10 +76,10 @@
     );
   });
 
-  // 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 @@
       'build',
       options: <String>[
         'ios-framework',
-        '--universal',
         '--verbose',
         '--output=$outputDirectoryName'
       ],
@@ -110,40 +109,6 @@
 
   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 @@
     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 <String>['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);
@@ -233,18 +221,11 @@
     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(
@@ -263,21 +244,14 @@
     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,12 +260,16 @@
       'Flutter.framework',
       'Flutter',
     ));
-  }
 
-  section("Check all modes' engine header");
-
-  for (final String mode in <String>['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');
@@ -300,20 +278,12 @@
     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,23 +332,16 @@
     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,
       'FlutterPluginRegistrant.xcframework',
       localXcodeArmDirectoryName,
       'FlutterPluginRegistrant.framework',
@@ -412,7 +375,6 @@
       options: <String>[
         'ios-framework',
         '--cocoapods',
-        '--universal',
         '--force', // Allow podspec creation on master.
         '--output=$cocoapodsOutputDirectoryName'
       ],
@@ -430,29 +392,29 @@
     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<void> _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<void> _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 8e580a3..2d971b6 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 @@
               '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 @@
   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 @@
       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 @@
         _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 @@
     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 @@
     } finally {
       status.stop();
     }
-
-    await _produceUniversalFromXCFramework(buildInfo, flutterFrameworkCopy);
   }
 
   Future<void> _produceAppFramework(
@@ -347,7 +338,7 @@
     const String appFrameworkName = 'App.framework';
 
     final Status status = globals.logger.startProgress(
-      ' ├─Building App.framework...',
+      ' ├─Building App.xcframework...',
     );
     final List<EnvironmentType> environmentTypes = <EnvironmentType>[
       EnvironmentType.physical,
@@ -407,14 +398,13 @@
               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 @@
                   .childDirectory(podFrameworkName)
           ];
 
-          await _produceUniversalFramework(frameworks, binaryName, modeDirectory);
           await _produceXCFramework(frameworks, binaryName, modeDirectory);
         }
       }
@@ -529,37 +518,6 @@
     }
   }
 
-  Future<void> _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<Directory> frameworks = xcframework
-            .listSync()
-            .whereType<Directory>()
-            .map((Directory triple) => triple
-                .listSync()
-                .whereType<Directory>()
-                .firstWhere((Directory frameworkDirectory) =>
-                    frameworkDirectory.basename ==
-                    '$frameworkBinaryName.framework'));
-
-        await _produceUniversalFramework(
-            frameworks, frameworkBinaryName, xcframework.parent);
-      } finally {
-        status.stop();
-      }
-    }
-
-    if (!boolArg('xcframework')) {
-      xcframework.deleteSync(recursive: true);
-    }
-  }
-
   Future<void> _produceXCFramework(Iterable<Directory> frameworks,
       String frameworkBinaryName, Directory outputDirectory) async {
     if (!boolArg('xcframework')) {
@@ -587,42 +545,4 @@
           'Unable to create $frameworkBinaryName.xcframework: ${xcframeworkResult.stderr}');
     }
   }
-
-  Future<void> _produceUniversalFramework(Iterable<Directory> 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<String> lipoCommand = <String>[
-      ...globals.xcode.xcrunCommand(),
-      'lipo',
-      '-create',
-      for (Directory framework in frameworks) ...<String>[
-        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}');
-    }
-  }
 }