Run more xcodebuild commands in native arm on Apple Silicon (#69837)

diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 043179e..0a39a87 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -10,6 +10,7 @@
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
+import '../base/os.dart';
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../base/terminal.dart';
@@ -242,26 +243,32 @@
       _terminal = terminal,
       _logger = logger,
       _processUtils = ProcessUtils(logger: logger, processManager: processManager),
+      _operatingSystemUtils = OperatingSystemUtils(
+        fileSystem: fileSystem,
+        logger: logger,
+        platform: platform,
+        processManager: processManager,
+      ),
       _usage = usage;
 
   final Platform _platform;
   final FileSystem _fileSystem;
   final ProcessUtils _processUtils;
+  final OperatingSystemUtils _operatingSystemUtils;
   final Terminal _terminal;
   final Logger _logger;
   final Usage _usage;
 
-  static const String _executable = '/usr/bin/xcodebuild';
   static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)');
 
   void _updateVersion() {
-    if (!_platform.isMacOS || !_fileSystem.file(_executable).existsSync()) {
+    if (!_platform.isMacOS || !_fileSystem.file('/usr/bin/xcodebuild').existsSync()) {
       return;
     }
     try {
       if (_versionText == null) {
         final RunResult result = _processUtils.runSync(
-          <String>[_executable, '-version'],
+          <String>[...xcrunCommand(), 'xcodebuild', '-version'],
         );
         if (result.exitCode != 0) {
           return;
@@ -316,6 +323,25 @@
     return _patchVersion;
   }
 
+  /// The `xcrun` Xcode command to run or locate development
+  /// tools and properties.
+  ///
+  /// Returns `xcrun` on x86 macOS.
+  /// Returns `/usr/bin/arch -arm64e xcrun` on ARM macOS to force Xcode commands
+  /// to run outside the x86 Rosetta translation, which may cause crashes.
+  List<String> xcrunCommand() {
+    final List<String> xcrunCommand = <String>[];
+    if (_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm) {
+      // Force Xcode commands to run outside Rosetta.
+      xcrunCommand.addAll(<String>[
+        '/usr/bin/arch',
+        '-arm64e',
+      ]);
+    }
+    xcrunCommand.add('xcrun');
+    return xcrunCommand;
+  }
+
   /// Asynchronously retrieve xcode build settings. This one is preferred for
   /// new call-sites.
   ///
@@ -331,7 +357,8 @@
       terminal: _terminal,
     );
     final List<String> showBuildSettingsCommand = <String>[
-      _executable,
+      ...xcrunCommand(),
+      'xcodebuild',
       '-project',
       _fileSystem.path.absolute(projectPath),
       if (scheme != null)
@@ -368,7 +395,8 @@
 
   Future<void> cleanWorkspace(String workspacePath, String scheme, { bool verbose = false }) async {
     await _processUtils.run(<String>[
-      _executable,
+      ...xcrunCommand(),
+      'xcodebuild',
       '-workspace',
       workspacePath,
       '-scheme',
@@ -387,7 +415,8 @@
     const int missingProjectExitCode = 66;
     final RunResult result = await _processUtils.run(
       <String>[
-        _executable,
+        ...xcrunCommand(),
+        'xcodebuild',
         '-list',
         if (projectFilename != null) ...<String>['-project', projectFilename],
       ],
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart
index 887a768..edc167b 100644
--- a/packages/flutter_tools/lib/src/macos/xcode.dart
+++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -12,7 +12,6 @@
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
-import '../base/os.dart';
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../build_info.dart';
@@ -66,18 +65,11 @@
   })  : _platform = platform,
         _fileSystem = fileSystem,
         _xcodeProjectInterpreter = xcodeProjectInterpreter,
-        _operatingSystemUtils = OperatingSystemUtils(
-          fileSystem: fileSystem,
-          logger: logger,
-          platform: platform,
-          processManager: processManager,
-        ),
         _processUtils =
             ProcessUtils(logger: logger, processManager: processManager);
 
   final Platform _platform;
   final ProcessUtils _processUtils;
-  final OperatingSystemUtils _operatingSystemUtils;
   final FileSystem _fileSystem;
   final XcodeProjectInterpreter _xcodeProjectInterpreter;
 
@@ -169,24 +161,8 @@
     return false;
   }
 
-  /// The `xcrun` Xcode command to run or locate development
-  /// tools and properties.
-  ///
-  /// Returns `xcrun` on x86 macOS.
-  /// Returns `/usr/bin/arch -arm64e xcrun` on ARM macOS to force Xcode commands
-  /// to run outside the x86 Rosetta translation, which may cause crashes.
-  List<String> xcrunCommand() {
-    final List<String> xcrunCommand = <String>[];
-    if (_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm) {
-      // Force Xcode commands to run outside Rosetta.
-      xcrunCommand.addAll(<String>[
-        '/usr/bin/arch',
-        '-arm64e',
-      ]);
-    }
-    xcrunCommand.add('xcrun');
-    return xcrunCommand;
-  }
+  /// See [XcodeProjectInterpreter.xcrunCommand].
+  List<String> xcrunCommand() => _xcodeProjectInterpreter.xcrunCommand();
 
   Future<RunResult> cc(List<String> args) {
     return _processUtils.run(
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart
index 0355ca5..a500e63 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart
@@ -77,14 +77,6 @@
     'xattr', '-r', '-d', 'com.apple.FinderInfo', '/ios'
   ]);
 
-  const FakeCommand armCheckCommand = FakeCommand(
-    command: <String>[
-      'sysctl',
-      'hw.optional.arm64',
-    ],
-    exitCode: 1,
-  );
-
   // Creates a FakeCommand for the xcodebuild call to build the app
   // in the given configuration.
   FakeCommand setUpMockXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
@@ -215,7 +207,6 @@
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
       xattrCommand,
-      armCheckCommand,
       setUpMockXcodeBuildHandler(),
       setUpMockXcodeBuildHandler(showBuildSettings: true),
     ]),
@@ -234,7 +225,6 @@
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
       xattrCommand,
-      armCheckCommand,
       setUpMockXcodeBuildHandler(verbose: true),
       setUpMockXcodeBuildHandler(verbose: true, showBuildSettings: true),
     ]),
@@ -263,7 +253,6 @@
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
       xattrCommand,
-      armCheckCommand,
       setUpMockXcodeBuildHandler(onRun: () {
         fileSystem.file('build/flutter_size_01/snapshot.arm64.json')
           ..createSync(recursive: true)
@@ -311,7 +300,6 @@
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
           xattrCommand,
-          armCheckCommand,
           setUpMockXcodeBuildHandler(),
           setUpMockXcodeBuildHandler(showBuildSettings: true),
           exportArchiveCommand,
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart
index 59c640b..772a1cf 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart
@@ -478,13 +478,6 @@
         '--lazy-async-stacks',
         '$build/app.dill',
       ]),
-      const FakeCommand(
-        command: <String>[
-          'sysctl',
-          'hw.optional.arm64',
-        ],
-        exitCode: 1,
-      ),
       FakeCommand(command: <String>[
         'xcrun',
         'cc',
@@ -593,13 +586,6 @@
         '--lazy-async-stacks',
         '$build/app.dill',
       ]),
-      const FakeCommand(
-        command: <String>[
-          'sysctl',
-          'hw.optional.arm64',
-        ],
-        exitCode: 1,
-      ),
       FakeCommand(command: <String>[
         'xcrun',
         'cc',
@@ -677,13 +663,6 @@
         '--lazy-async-stacks',
         '$build/app.dill',
       ]),
-      const FakeCommand(
-        command: <String>[
-          'sysctl',
-          'hw.optional.arm64',
-        ],
-        exitCode: 1,
-      ),
       FakeCommand(command: <String>[
         'xcrun',
         'cc',
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
index e953ffa..5b4f1dc 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart
@@ -71,14 +71,6 @@
   testUsingContext('DebugUniveralFramework creates expected binary with arm64 only arch', () async {
     environment.defines[kIosArchs] = 'arm64';
     processManager.addCommands(<FakeCommand>[
-      // Create iphone stub.
-      const FakeCommand(
-        command: <String>[
-          'sysctl',
-          'hw.optional.arm64',
-        ],
-        exitCode: 1,
-      ),
       const FakeCommand(command: <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path']),
       FakeCommand(command: <String>[
         'xcrun',
diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
index c324482..d81fa7d 100644
--- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
@@ -51,7 +51,9 @@
 
     // Work around https://github.com/flutter/flutter/issues/56415.
     testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () {
-      when(processManager.runSync(<String>[xcodebuild, '-version']))
+      when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
+          .thenReturn(ProcessResult(0, 1, '', ''));
+      when(processManager.runSync(<String>['xcrun', 'xcodebuild', '-version']))
         .thenThrow(const ProcessException(xcodebuild, <String>['-version']));
 
       expect(xcodeProjectInterpreter.versionText, isNull);
@@ -65,6 +67,9 @@
       );
       platform.environment = const <String, String>{};
 
+      when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
+          .thenReturn(ProcessResult(0, 1, '', ''));
+
       expect(await xcodeProjectInterpreter.getBuildSettings(
         '', scheme: 'Runner', timeout: delay),
         const <String, String>{});
@@ -75,6 +80,14 @@
     });
   });
 
+  const FakeCommand kARMCheckCommand = FakeCommand(
+    command: <String>[
+      'sysctl',
+      'hw.optional.arm64',
+    ],
+    exitCode: 1,
+  );
+
   FakeProcessManager fakeProcessManager;
   XcodeProjectInterpreter xcodeProjectInterpreter;
   FakePlatform platform;
@@ -102,43 +115,55 @@
   });
 
   testWithoutContext('xcodebuild versionText returns null when xcodebuild is not fully installed', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
-        "but active developer directory '/Library/Developer/CommandLineTools' "
-        'is a command line tools instance',
-      exitCode: 1,
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
+            "but active developer directory '/Library/Developer/CommandLineTools' "
+            'is a command line tools instance',
+        exitCode: 1,
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.versionText, isNull);
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
   testWithoutContext('xcodebuild versionText returns formatted version text', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b');
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
   testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b');
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
   testWithoutContext('xcodebuild version parts can be parsed', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode 11.4.1\nBuild version 11N111s',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode 11.4.1\nBuild version 11N111s',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.majorVersion, 11);
     expect(xcodeProjectInterpreter.minorVersion, 4);
@@ -147,10 +172,13 @@
   });
 
   testWithoutContext('xcodebuild minor and patch version default to 0', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode 11\nBuild version 11N111s',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode 11\nBuild version 11N111s',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.majorVersion, 11);
     expect(xcodeProjectInterpreter.minorVersion, 0);
@@ -159,10 +187,13 @@
   });
 
   testWithoutContext('xcodebuild version parts is null when version has unexpected format', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
+      ),
+    ]);
     expect(xcodeProjectInterpreter.majorVersion, isNull);
     expect(xcodeProjectInterpreter.minorVersion, isNull);
     expect(xcodeProjectInterpreter.patchVersion, isNull);
@@ -192,53 +223,92 @@
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
-  testWithoutContext('xcodebuild isInstalled is false when Xcode is not fully installed', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
-        "but active developer directory '/Library/Developer/CommandLineTools' "
-        'is a command line tools instance',
-      exitCode: 1,
-    ));
+  testWithoutContext(
+      'xcodebuild isInstalled is false when Xcode is not fully installed', () {
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: "xcode-select: error: tool 'xcodebuild' requires Xcode, "
+            "but active developer directory '/Library/Developer/CommandLineTools' "
+            'is a command line tools instance',
+        exitCode: 1,
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.isInstalled, isFalse);
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
   testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode Ultra5000\nBuild version 8E3004b',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.isInstalled, isFalse);
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
   testWithoutContext('xcodebuild isInstalled is true when version has expected format', () {
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-version'],
-      stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-version'],
+        stdout: 'Xcode 8.3.3\nBuild version 8E3004b',
+      ),
+    ]);
 
     expect(xcodeProjectInterpreter.isInstalled, isTrue);
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
 
+  testWithoutContext('xcrun runs natively on arm64', () {
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      FakeCommand(
+        command: <String>[
+          'sysctl',
+          'hw.optional.arm64',
+        ],
+        stdout: 'hw.optional.arm64: 1',
+      ),
+    ]);
+
+    expect(xcodeProjectInterpreter.xcrunCommand(), <String>[
+      '/usr/bin/arch',
+      '-arm64e',
+      'xcrun',
+    ]);
+    expect(fakeProcessManager.hasRemainingExpectations, isFalse);
+  });
+
   testWithoutContext('xcodebuild build settings is empty when xcodebuild failed to get the build settings', () async {
     platform.environment = const <String, String>{};
 
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[
-        '/usr/bin/xcodebuild',
-        '-project',
-        '/',
-        '-scheme',
-        'Free',
-        '-showBuildSettings'
-      ],
-      exitCode: 1,
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      FakeCommand(
+        command: <String>[
+          'sysctl',
+          'hw.optional.arm64',
+        ],
+        exitCode: 1,
+      ),
+      FakeCommand(
+        command: <String>[
+          'xcrun',
+          'xcodebuild',
+          '-project',
+          '/',
+          '-scheme',
+          'Free',
+          '-showBuildSettings'
+        ],
+        exitCode: 1,
+      ),
+    ]);
 
     expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
@@ -247,15 +317,19 @@
   testWithoutContext('build settings accepts an empty scheme', () async {
     platform.environment = const <String, String>{};
 
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[
-        '/usr/bin/xcodebuild',
-        '-project',
-        '/',
-        '-showBuildSettings'
-      ],
-      exitCode: 1,
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>[
+          'xcrun',
+          'xcodebuild',
+          '-project',
+          '/',
+          '-showBuildSettings'
+        ],
+        exitCode: 1,
+      ),
+    ]);
 
     expect(await xcodeProjectInterpreter.getBuildSettings(''), const <String, String>{});
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
@@ -266,18 +340,22 @@
       'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
       'FLUTTER_XCODE_ARCHS': 'arm64'
     };
-    fakeProcessManager.addCommand(FakeCommand(
-      command: <String>[
-        xcodebuild,
-        '-project',
-        fileSystem.path.separator,
-        '-scheme',
-        'Free',
-        '-showBuildSettings',
-        'CODE_SIGN_STYLE=Manual',
-        'ARCHS=arm64'
-      ],
-    ));
+    fakeProcessManager.addCommands(<FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>[
+          'xcrun',
+          'xcodebuild',
+          '-project',
+          fileSystem.path.separator,
+          '-scheme',
+          'Free',
+          '-showBuildSettings',
+          'CODE_SIGN_STYLE=Manual',
+          'ARCHS=arm64'
+        ],
+      ),
+    ]);
     expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
   });
@@ -288,19 +366,23 @@
       'FLUTTER_XCODE_ARCHS': 'arm64'
     };
 
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[
-        xcodebuild,
-        '-workspace',
-        'workspace_path',
-        '-scheme',
-        'Free',
-        '-quiet',
-        'clean',
-        'CODE_SIGN_STYLE=Manual',
-        'ARCHS=arm64'
-      ],
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>[
+          'xcrun',
+          'xcodebuild',
+          '-workspace',
+          'workspace_path',
+          '-scheme',
+          'Free',
+          '-quiet',
+          'clean',
+          'CODE_SIGN_STYLE=Manual',
+          'ARCHS=arm64'
+        ],
+      ),
+    ]);
 
     await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free');
     expect(fakeProcessManager.hasRemainingExpectations, isFalse);
@@ -308,9 +390,12 @@
 
   testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async {
     const String workingDirectory = '/';
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-list'],
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-list'],
+      ),
+    ]);
 
     final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter(
       logger: logger,
@@ -329,11 +414,14 @@
     const String workingDirectory = '/';
     const String stderr = 'Useful Xcode failure message about missing project.';
 
-    fakeProcessManager.addCommand(const FakeCommand(
-      command: <String>[xcodebuild, '-list'],
-      exitCode: 66,
-      stderr: stderr,
-    ));
+    fakeProcessManager.addCommands(const <FakeCommand>[
+      kARMCheckCommand,
+      FakeCommand(
+        command: <String>['xcrun', 'xcodebuild', '-list'],
+        exitCode: 66,
+        stderr: stderr,
+      ),
+    ]);
 
     final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter(
       logger: logger,
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
index badb15e..71b429a 100644
--- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
@@ -39,14 +39,16 @@
 
     group('Xcode', () {
       Xcode xcode;
+      MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
 
       setUp(() {
+        mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
         xcode = Xcode(
           logger: logger,
           platform: FakePlatform(operatingSystem: 'macos'),
           fileSystem: MemoryFileSystem.test(),
           processManager: processManager,
-          xcodeProjectInterpreter: MockXcodeProjectInterpreter(),
+          xcodeProjectInterpreter: mockXcodeProjectInterpreter,
         );
       });
 
@@ -61,6 +63,8 @@
       });
 
       testWithoutContext('eulaSigned is false when clang is not installed', () {
+        when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
+
         when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
             .thenReturn(ProcessResult(123, 1, '', ''));
         when(processManager.runSync(<String>['xcrun', 'clang']))
@@ -140,6 +144,7 @@
 
         setUp(() {
           mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
+          when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
           platform = FakePlatform(operatingSystem: 'macos');
           xcode = Xcode(
             logger: logger,
@@ -252,25 +257,6 @@
           expect(fakeProcessManager.hasRemainingExpectations, isFalse);
         });
 
-        testWithoutContext('xcrun runs natively on arm64', () {
-          fakeProcessManager.addCommands(const <FakeCommand>[
-            FakeCommand(
-              command: <String>[
-                'sysctl',
-                'hw.optional.arm64',
-              ],
-              stdout: 'hw.optional.arm64: 1',
-            ),
-          ]);
-
-          expect(xcode.xcrunCommand(), <String>[
-            '/usr/bin/arch',
-            '-arm64e',
-            'xcrun',
-          ]);
-          expect(fakeProcessManager.hasRemainingExpectations, isFalse);
-        });
-
         testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
           fakeProcessManager.addCommand(const FakeCommand(
             command: <String>['/usr/bin/xcode-select', '--print-path'],
@@ -288,13 +274,6 @@
         testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
           fakeProcessManager.addCommands(const <FakeCommand>[
             FakeCommand(
-              command: <String>[
-                'sysctl',
-                'hw.optional.arm64',
-              ],
-              exitCode: 1,
-            ),
-            FakeCommand(
               command: <String>['xcrun', 'clang'],
               exitCode: 1,
               stderr:
@@ -310,13 +289,6 @@
           fakeProcessManager.addCommands(
             const <FakeCommand>[
               FakeCommand(
-                command: <String>[
-                  'sysctl',
-                  'hw.optional.arm64',
-                ],
-                exitCode: 1,
-              ),
-              FakeCommand(
                 command: <String>['xcrun', 'clang'],
                 exitCode: 1,
                 stderr: 'clang: error: no input files',
@@ -334,18 +306,6 @@
         });
 
         group('SDK location', () {
-          setUp(() {
-            fakeProcessManager.addCommand(
-              const FakeCommand(
-                command: <String>[
-                  'sysctl',
-                  'hw.optional.arm64',
-                ],
-                exitCode: 1,
-              ),
-            );
-          });
-
           const String sdkroot = 'Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk';
 
           testWithoutContext('--show-sdk-path iphoneos', () async {
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 6adb7dc..0813ecc 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -414,6 +414,9 @@
       BufferLogger.test(),
     );
   }
+
+  @override
+  List<String> xcrunCommand() => <String>['xcrun'];
 }
 
 class MockFlutterVersion extends Mock implements FlutterVersion {