Simplify iOS debug build (#17145)

iOS debug builds always run in interpreted mode whether on device or on
simulator. In both cases, we can skip snapshotting and link against an
empty App.framework. Previously, we did this for iOS simulator builds.
This does the same for device builds.

Previously, debug iOS builds used gen_snapshot to generate a core
snapshot, then used 'xxd' to generate C files containing the snapshot
data in buffers named kDartVmSnapshotData and kDartIsolateSnapshotData,
which are then compiled/linked into App.framework. This is unnecessary
since the VM compiled into Flutter.framework already contains this data.
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
index f8ddb8d..6ed8833 100755
--- a/packages/flutter_tools/bin/xcode_backend.sh
+++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -109,7 +109,7 @@
     preview_dart_2_flag="--no-preview-dart-2"
   fi
 
-  if [[ "$CURRENT_ARCH" != "x86_64" ]]; then
+  if [[ "${build_mode}" != "debug" ]]; then
     StreamOutput " ├─Building Dart code..."
     RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics           \
       ${verbose_flag}                                                       \
@@ -131,6 +131,7 @@
   else
     RunCommand mkdir -p -- "${derived_dir}/App.framework"
     RunCommand eval "$(echo "static const int Moo = 88;" | xcrun clang -x c \
+        -arch "$CURRENT_ARCH" \
         -dynamiclib \
         -Xlinker -rpath -Xlinker '@executable_path/Frameworks' \
         -Xlinker -rpath -Xlinker '@loader_path/Frameworks' \
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index 3a2f609..d66a37e 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -221,14 +221,10 @@
     outputDir.createSync(recursive: true);
 
     printTrace('Compiling Dart to kernel: $mainPath');
-    final bool aot = !_isInterpreted(platform, buildMode);
-    final List<String> entryPointsJsonFiles = <String>[];
-    if (aot) {
-      entryPointsJsonFiles.addAll(<String>[
-        artifacts.getArtifactPath(Artifact.entryPointsJson, platform, buildMode),
-        artifacts.getArtifactPath(Artifact.entryPointsExtraJson, platform, buildMode),
-      ]);
-    }
+    final List<String> entryPointsJsonFiles = <String>[
+      artifacts.getArtifactPath(Artifact.entryPointsJson, platform, buildMode),
+      artifacts.getArtifactPath(Artifact.entryPointsExtraJson, platform, buildMode),
+    ];
 
     if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty)
       printTrace('Extra front-end options: $extraFrontEndOptions');
@@ -241,7 +237,7 @@
       depFilePath: depfilePath,
       extraFrontEndOptions: extraFrontEndOptions,
       linkPlatformKernelIn: true,
-      aot: aot,
+      aot: true,
       entryPointsJsonFiles: entryPointsJsonFiles,
       trackWidgetCreation: false,
     );
@@ -264,9 +260,9 @@
     @required bool preferSharedLibrary,
     List<String> extraGenSnapshotOptions: const <String>[],
   }) async {
-    if (!_isValidAotPlatform(platform)) {
+    if (!_isValidAotPlatform(platform, buildMode)) {
       printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
-      return -2;
+      return -1;
     }
 
     final bool compileToSharedLibrary = preferSharedLibrary && androidSdk.ndkCompiler != null;
@@ -278,7 +274,7 @@
     final String packageMapError = packageMap.checkValid();
     if (packageMapError != null) {
       printError(packageMapError);
-      return -3;
+      return -2;
     }
 
     final Directory outputDir = fs.directory(outputPath);
@@ -287,8 +283,10 @@
     final String skyEnginePkg = _getPackagePath(packageMap, 'sky_engine');
     final String uiPath = fs.path.join(skyEnginePkg, 'lib', 'ui', 'ui.dart');
     final String vmServicePath = fs.path.join(skyEnginePkg, 'sdk_ext', 'vmservice_io.dart');
+    final String vmEntryPoints = artifacts.getArtifactPath(Artifact.dartVmEntryPointsTxt, platform, buildMode);
+    final String ioEntryPoints = artifacts.getArtifactPath(Artifact.dartIoEntriesTxt, platform, buildMode);
 
-    final List<String> inputPaths = <String>[uiPath, vmServicePath, mainPath];
+    final List<String> inputPaths = <String>[uiPath, vmServicePath, vmEntryPoints, ioEntryPoints, mainPath];
     final Set<String> outputPaths = new Set<String>();
 
     final String vmSnapshotData = fs.path.join(outputDir.path, 'vm_snapshot_data');
@@ -299,6 +297,8 @@
       '--isolate_snapshot_data=$isolateSnapshotData',
       '--url_mapping=dart:ui,$uiPath',
       '--url_mapping=dart:vmservice_io,$vmServicePath',
+      '--embedder_entry_points_manifest=$vmEntryPoints',
+      '--embedder_entry_points_manifest=$ioEntryPoints',
       '--dependencies=$depfilePath',
     ];
     if (previewDart2) {
@@ -318,21 +318,6 @@
       genSnapshotArgs.addAll(extraGenSnapshotOptions);
     }
 
-    final bool interpreter = _isInterpreted(platform, buildMode);
-    if (!interpreter) {
-      final String vmEntryPoints = artifacts.getArtifactPath(Artifact.dartVmEntryPointsTxt, platform, buildMode);
-      final String ioEntryPoints = artifacts.getArtifactPath(Artifact.dartIoEntriesTxt, platform, buildMode);
-      genSnapshotArgs.add('--embedder_entry_points_manifest=$vmEntryPoints');
-      genSnapshotArgs.add('--embedder_entry_points_manifest=$ioEntryPoints');
-      inputPaths..addAll(<String>[vmEntryPoints, ioEntryPoints]);
-    }
-
-    // iOS snapshot generated files, compiled object files.
-    const String kVmSnapshotData = 'kDartVmSnapshotData';
-    const String kIsolateSnapshotData = 'kDartIsolateSnapshotData';
-    final String kVmSnapshotDataO = fs.path.join(outputDir.path, '$kVmSnapshotData.o');
-    final String kIsolateSnapshotDataO = fs.path.join(outputDir.path, '$kIsolateSnapshotData.o');
-
     // Compile-to-assembly outputs.
     final String assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
     final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o');
@@ -365,14 +350,9 @@
         }
         break;
       case TargetPlatform.ios:
-        if (interpreter) {
-          genSnapshotArgs.add('--snapshot_kind=core');
-          outputPaths.addAll(<String>[kVmSnapshotDataO, kIsolateSnapshotDataO]);
-        } else {
-          genSnapshotArgs.add('--snapshot_kind=app-aot-assembly');
-          genSnapshotArgs.add('--assembly=$assembly');
-          outputPaths.add(assemblyO);
-        }
+        genSnapshotArgs.add('--snapshot_kind=app-aot-assembly');
+        genSnapshotArgs.add('--assembly=$assembly');
+        outputPaths.add(assemblyO);
         break;
       case TargetPlatform.darwin_x64:
       case TargetPlatform.linux_x64:
@@ -382,19 +362,13 @@
         assert(false);
     }
 
-    if (interpreter) {
-      final String snapshotDartIOS = artifacts.getArtifactPath(Artifact.snapshotDart, platform, buildMode);
-      inputPaths.add(snapshotDartIOS);
-      genSnapshotArgs.add(snapshotDartIOS);
-    } else {
-      genSnapshotArgs.add(mainPath);
-    }
+    genSnapshotArgs.add(mainPath);
 
     // Verify that all required inputs exist.
     final Iterable<String> missingInputs = inputPaths.where((String p) => !fs.isFileSync(p));
     if (missingInputs.isNotEmpty) {
       printError('Missing input files: $missingInputs from $inputPaths');
-      return -4;
+      return -3;
     }
 
     // If inputs and outputs have not changed since last run, skip the build.
@@ -413,7 +387,7 @@
     );
     if (genSnapshotExitCode != 0) {
       printError('Dart snapshot generator failed with exit code $genSnapshotExitCode');
-      return -6;
+      return -4;
     }
 
     // Write path to gen_snapshot, since snapshots have to be re-generated when we roll
@@ -426,26 +400,7 @@
       printStatus('Building App.framework...');
 
       const List<String> commonBuildOptions = const <String>['-arch', 'arm64', '-miphoneos-version-min=8.0'];
-      if (interpreter) {
-        final String kVmSnapshotDataC = fs.path.join(outputDir.path, '$kVmSnapshotData.c');
-        final String kIsolateSnapshotDataC = fs.path.join(outputDir.path, '$kIsolateSnapshotData.c');
-        await fs.file(vmSnapshotData).rename(fs.path.join(outputDir.path, kVmSnapshotData));
-        await fs.file(isolateSnapshotData).rename(fs.path.join(outputDir.path, kIsolateSnapshotData));
-
-        await xxd.run(
-          <String>['--include', kVmSnapshotData, fs.path.basename(kVmSnapshotDataC)],
-          workingDirectory: outputDir.path,
-        );
-        await xxd.run(
-          <String>['--include', kIsolateSnapshotData, fs.path.basename(kIsolateSnapshotDataC)],
-          workingDirectory: outputDir.path,
-        );
-
-        await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', kVmSnapshotDataC, '-o', kVmSnapshotDataO]));
-        await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', kIsolateSnapshotDataC, '-o', kIsolateSnapshotDataO]));
-      } else {
-        await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', assembly, '-o', assemblyO]));
-      }
+      await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', assembly, '-o', assemblyO]));
 
       final String frameworkDir = fs.path.join(outputDir.path, 'App.framework');
       fs.directory(frameworkDir).createSync(recursive: true);
@@ -456,13 +411,8 @@
           '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
           '-install_name', '@rpath/App.framework/App',
           '-o', appLib,
+          assemblyO,
       ]);
-      if (interpreter) {
-        linkArgs.add(kVmSnapshotDataO);
-        linkArgs.add(kIsolateSnapshotDataO);
-      } else {
-        linkArgs.add(assemblyO);
-      }
       await xcode.clang(linkArgs);
     } else {
       if (compileToSharedLibrary) {
@@ -484,7 +434,9 @@
     return 0;
   }
 
-  bool _isValidAotPlatform(TargetPlatform platform) {
+  bool _isValidAotPlatform(TargetPlatform platform, BuildMode buildMode) {
+    if (platform == TargetPlatform.ios && buildMode == BuildMode.debug)
+      return false;
     return const <TargetPlatform>[
       TargetPlatform.android_arm,
       TargetPlatform.android_arm64,
@@ -492,11 +444,6 @@
     ].contains(platform);
   }
 
-  /// Returns true if the specified platform and build mode require running in interpreted mode.
-  bool _isInterpreted(TargetPlatform platform, BuildMode buildMode) {
-    return platform == TargetPlatform.ios && buildMode == BuildMode.debug;
-  }
-
   String _getPackagePath(PackageMap packageMap, String package) {
     return fs.path.dirname(fs.path.fromUri(packageMap.map[package]));
   }
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index fb1f650..28d2035 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -74,7 +74,6 @@
       Usage: () => new Usage(),
       Xcode: () => new Xcode(),
       XcodeProjectInterpreter: () => new XcodeProjectInterpreter(),
-      Xxd: () => new Xxd(),
     },
   );
 }
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 3b94deb..7d1c8ba 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -39,8 +39,6 @@
 
 Xcode get xcode => context[Xcode];
 
-Xxd get xxd => context[Xxd];
-
 class PythonModule {
   const PythonModule(this.name);
 
@@ -105,12 +103,6 @@
   }
 }
 
-class Xxd {
-  Future<RunResult> run(List<String> args, {String workingDirectory}) {
-    return runCheckedAsync(<String>['xxd']..addAll(args), workingDirectory: workingDirectory);
-  }
-}
-
 class Xcode {
   bool get isInstalledAndMeetsVersionCheck => isInstalled && isVersionSatisfactory;
 
diff --git a/packages/flutter_tools/test/base/build_test.dart b/packages/flutter_tools/test/base/build_test.dart
index e58c87f..4c286ea 100644
--- a/packages/flutter_tools/test/base/build_test.dart
+++ b/packages/flutter_tools/test/base/build_test.dart
@@ -22,7 +22,6 @@
 class MockFlutterVersion extends Mock implements FlutterVersion {}
 class MockArtifacts extends Mock implements Artifacts {}
 class MockXcode extends Mock implements Xcode {}
-class MockXxd extends Mock implements Xxd {}
 
 class _FakeGenSnapshot implements GenSnapshot {
   _FakeGenSnapshot({
@@ -586,7 +585,6 @@
     Snapshotter snapshotter;
     MockArtifacts mockArtifacts;
     MockXcode mockXcode;
-    MockXxd mockXxd;
 
     setUp(() async {
       fs = new MemoryFileSystem();
@@ -608,7 +606,6 @@
       snapshotter = new Snapshotter();
       mockArtifacts = new MockArtifacts();
       mockXcode = new MockXcode();
-      mockXxd = new MockXxd();
       for (BuildMode mode in BuildMode.values) {
         when(mockArtifacts.getArtifactPath(Artifact.dartVmEntryPointsTxt, TargetPlatform.ios, mode)).thenReturn(kVmEntrypoints);
         when(mockArtifacts.getArtifactPath(Artifact.dartIoEntriesTxt, TargetPlatform.ios, mode)).thenReturn(kIoEntries);
@@ -623,25 +620,11 @@
       FileSystem: () => fs,
       GenSnapshot: () => genSnapshot,
       Xcode: () => mockXcode,
-      Xxd: () => mockXxd,
     };
 
-    testUsingContext('builds iOS debug AOT snapshot', () async {
-      fs.file('main.dill').writeAsStringSync('binary magic');
-
+    testUsingContext('iOS debug AOT snapshot is invalid', () async {
       final String outputPath = fs.path.join('build', 'foo');
-      fs.directory(outputPath).createSync(recursive: true);
-
-      genSnapshot.outputs = <String, String>{
-        fs.path.join(outputPath, 'vm_snapshot_data'): '',
-        fs.path.join(outputPath, 'vm_snapshot_instr'): '',
-        fs.path.join(outputPath, 'isolate_snapshot_data'): '',
-        fs.path.join(outputPath, 'isolate_snapshot_instr'): '',
-        fs.path.join(outputPath, 'snapshot.d'): '',
-        fs.path.join(outputPath, 'snapshot_assembly.S'): '',
-      };
-
-      final int genSnapshotExitCode = await snapshotter.buildAotSnapshot(
+      expect(await snapshotter.buildAotSnapshot(
         platform: TargetPlatform.ios,
         buildMode: BuildMode.debug,
         mainPath: 'main.dill',
@@ -649,26 +632,7 @@
         outputPath: outputPath,
         preferSharedLibrary: false,
         previewDart2: true,
-      );
-
-      expect(genSnapshotExitCode, 0);
-      expect(genSnapshot.callCount, 1);
-      expect(genSnapshot.snapshotType.platform, TargetPlatform.ios);
-      expect(genSnapshot.snapshotType.mode, BuildMode.debug);
-      expect(genSnapshot.packagesPath, '.packages');
-      expect(genSnapshot.additionalArgs, <String>[
-        '--vm_snapshot_data=${fs.path.join(outputPath, 'vm_snapshot_data')}',
-        '--isolate_snapshot_data=${fs.path.join(outputPath, 'isolate_snapshot_data')}',
-        '--url_mapping=dart:ui,${fs.path.join(skyEnginePath, 'lib', 'ui', 'ui.dart')}',
-        '--url_mapping=dart:vmservice_io,${fs.path.join(skyEnginePath, 'sdk_ext', 'vmservice_io.dart')}',
-        '--dependencies=${fs.path.join(outputPath, 'snapshot.d')}',
-        '--reify-generic-functions',
-        '--strong',
-        '--no-checked',
-        '--conditional_directives',
-        '--snapshot_kind=core',
-        'snapshot.dart',
-      ]);
+      ), isNot(equals(0)));
     }, overrides: contextOverrides);
 
     testUsingContext('builds iOS profile AOT snapshot', () async {
@@ -702,13 +666,13 @@
         '--isolate_snapshot_data=${fs.path.join(outputPath, 'isolate_snapshot_data')}',
         '--url_mapping=dart:ui,${fs.path.join(skyEnginePath, 'lib', 'ui', 'ui.dart')}',
         '--url_mapping=dart:vmservice_io,${fs.path.join(skyEnginePath, 'sdk_ext', 'vmservice_io.dart')}',
+        '--embedder_entry_points_manifest=$kVmEntrypoints',
+        '--embedder_entry_points_manifest=$kIoEntries',
         '--dependencies=${fs.path.join(outputPath, 'snapshot.d')}',
         '--reify-generic-functions',
         '--strong',
         '--no-checked',
         '--conditional_directives',
-        '--embedder_entry_points_manifest=$kVmEntrypoints',
-        '--embedder_entry_points_manifest=$kIoEntries',
         '--snapshot_kind=app-aot-assembly',
         '--assembly=${fs.path.join(outputPath, 'snapshot_assembly.S')}',
         'main.dill',
@@ -746,11 +710,11 @@
         '--isolate_snapshot_data=${fs.path.join(outputPath, 'isolate_snapshot_data')}',
         '--url_mapping=dart:ui,${fs.path.join(skyEnginePath, 'lib', 'ui', 'ui.dart')}',
         '--url_mapping=dart:vmservice_io,${fs.path.join(skyEnginePath, 'sdk_ext', 'vmservice_io.dart')}',
+        '--embedder_entry_points_manifest=$kVmEntrypoints',
+        '--embedder_entry_points_manifest=$kIoEntries',
         '--dependencies=${fs.path.join(outputPath, 'snapshot.d')}',
         '--reify-generic-functions',
         '--strong',
-        '--embedder_entry_points_manifest=$kVmEntrypoints',
-        '--embedder_entry_points_manifest=$kIoEntries',
         '--snapshot_kind=app-aot-assembly',
         '--assembly=${fs.path.join(outputPath, 'snapshot_assembly.S')}',
         'main.dill',