Revert "[flutter_tools] initialize frontend server with build (#49405)" (#51476)

This reverts commit 59cc3cdf0167740b51236f5eb5571c6bf3345d6f.
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
index 469f73e..606d5b2 100644
--- a/packages/flutter_tools/lib/src/compile.dart
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -469,6 +469,7 @@
   // See: https://github.com/flutter/flutter/issues/50494
   void addFileSystemRoot(String root);
 
+
   /// If invoked for the first time, it compiles Dart script identified by
   /// [mainPath], [invalidatedFiles] list is ignored.
   /// On successive runs [invalidatedFiles] indicates which files need to be
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart
index b985bff..3400c82 100644
--- a/packages/flutter_tools/lib/src/devfs.dart
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -488,9 +488,6 @@
     if (fullRestart) {
       generator.reset();
     }
-    // On a full restart, or on an initial compile for the attach based workflow,
-    // this will produce a full dill. Subsequent invocations will produce incremental
-    // dill files that depend on the invalidated files.
     globals.printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files');
     final CompilerOutput compilerOutput = await generator.recompile(
       mainPath,
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index d24e3c7..b58487e 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -629,6 +629,16 @@
     if (!artifactDirectory.existsSync()) {
       artifactDirectory.createSync(recursive: true);
     }
+    // TODO(jonahwilliams): this is a temporary work around to regain some of
+    // the initialize from dill performance. Longer term, we should have a
+    // better way to determine where the appropriate dill file is, as this
+    // doesn't work for Android or macOS builds.}
+    if (dillOutputPath == null) {
+      final File existingDill = globals.fs.file(globals.fs.path.join('build', 'app.dill'));
+      if (existingDill.existsSync()) {
+        existingDill.copySync(globals.fs.path.join(artifactDirectory.path, 'app.dill'));
+      }
+    }
   }
 
   @protected
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 32f98a6..67a93d3 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -16,7 +16,6 @@
 import 'base/logger.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
-import 'bundle.dart';
 import 'compile.dart';
 import 'convert.dart';
 import 'devfs.dart';
@@ -330,36 +329,14 @@
 
     firstBuildTime = DateTime.now();
 
-    final List<Future<bool>> startupTasks = <Future<bool>>[];
     for (final FlutterDevice device in flutterDevices) {
-      // Here we initialize the frontend_server concurrently with the platform
-      // build, reducing overall initialization time. This is safe because the first
-      // invocation of the frontend server produces a full dill file that the
-      // subsequent invocation in devfs will not overwrite.
-      if (device.generator != null) {
-        startupTasks.add(
-          device.generator.recompile(
-            mainPath,
-            <Uri>[],
-            outputPath: dillOutputPath ??
-              getDefaultApplicationKernelPath(trackWidgetCreation: device.trackWidgetCreation),
-            packagesFilePath : packagesFilePath,
-          ).then((CompilerOutput output) => output?.errorCount == 0)
-        );
-      }
-      startupTasks.add(device.runHot(
+      final int result = await device.runHot(
         hotRunner: this,
         route: route,
-      ).then((int result) => result == 0));
-    }
-    try {
-      final List<bool> results = await Future.wait(startupTasks);
-      if (!results.every((bool passed) => passed)) {
-        return 1;
+      );
+      if (result != 0) {
+        return result;
       }
-    } on Exception catch (err) {
-      globals.printError(err.toString());
-      return 1;
     }
 
     return attach(
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index 9a9535e..77f60ad 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -84,6 +84,47 @@
       ProcessManager: () => FakeProcessManager.any(),
     });
 
+    testUsingContext('Forces fast start off for devices that do not support it', () async {
+      final MockDevice mockDevice = MockDevice(TargetPlatform.android_arm);
+      when(mockDevice.name).thenReturn('mockdevice');
+      when(mockDevice.supportsFastStart).thenReturn(false);
+      when(mockDevice.supportsHotReload).thenReturn(true);
+      when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async => false);
+      when(deviceManager.hasSpecifiedAllDevices).thenReturn(false);
+      when(deviceManager.findTargetDevices(any)).thenAnswer((Invocation invocation) {
+        return Future<List<Device>>.value(<Device>[mockDevice]);
+      });
+      when(deviceManager.getDevices()).thenAnswer((Invocation invocation) {
+        return Future<List<Device>>.value(<Device>[mockDevice]);
+      });
+      globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
+      globals.fs.file('pubspec.yaml').createSync();
+      globals.fs.file('.packages').createSync();
+
+      final RunCommand command = RunCommand();
+      applyMocksToCommand(command);
+      try {
+        await createTestCommandRunner(command).run(<String>[
+          'run',
+          '--fast-start',
+          '--no-pub',
+        ]);
+        fail('Expect exception');
+      } catch (e) {
+        expect(e, isA<ToolExit>());
+      }
+
+      final BufferLogger bufferLogger = globals.logger as BufferLogger;
+      expect(bufferLogger.statusText, isNot(contains(
+        'Using --fast-start option with device mockdevice, but this device '
+        'does not support it. Overriding the setting to false.'
+      )));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => MemoryFileSystem(),
+      ProcessManager: () => FakeProcessManager.any(),
+      DeviceManager: () => MockDeviceManager(),
+    });
+
     testUsingContext('Walks upward looking for a pubspec.yaml and succeeds if found', () async {
       globals.fs.file('pubspec.yaml').createSync();
       globals.fs.file('.packages')
diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
index d570b08..f443115 100644
--- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
@@ -234,6 +234,10 @@
     Usage: () => MockUsage(),
   }));
 
+  test('ResidentRunner copies dill file from build output into temp directory', () => testbed.run(() async {
+    expect(residentRunner.artifactDirectory.childFile('app.dill').readAsStringSync(), 'ABC');
+  }));
+
   test('ResidentRunner can send target platform to analytics from hot reload', () => testbed.run(() async {
     when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
       return 'Example';
diff --git a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
index 612de2f..455e756 100644
--- a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
+++ b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
@@ -36,10 +36,10 @@
   });
 
   test('newly added code executes during hot reload', () async {
-    final StringBuffer stdout = StringBuffer();
-    final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
     await _flutter.run();
     _project.uncommentHotReloadPrint();
+    final StringBuffer stdout = StringBuffer();
+    final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
     try {
       await _flutter.hotReload();
       expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
@@ -49,10 +49,10 @@
   });
 
   test('reloadMethod triggers hot reload behavior', () async {
-    final StringBuffer stdout = StringBuffer();
-    final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
     await _flutter.run();
     _project.uncommentHotReloadPrint();
+    final StringBuffer stdout = StringBuffer();
+    final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
     try {
       final String libraryId = _project.buildBreakpointUri.toString();
       await _flutter.reloadMethod(libraryId: libraryId, classId: 'MyApp');
@@ -72,6 +72,7 @@
 
   test('breakpoints are hit after hot reload', () async {
     Isolate isolate;
+    await _flutter.run(withDebugger: true, startPaused: true);
     final Completer<void> sawTick1 = Completer<void>();
     final Completer<void> sawTick3 = Completer<void>();
     final Completer<void> sawDebuggerPausedMessage = Completer<void>();
@@ -91,7 +92,6 @@
         }
       },
     );
-    await _flutter.run(withDebugger: true, startPaused: true);
     await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
     unawaited(sawTick1.future.timeout(
       const Duration(seconds: 5),
@@ -125,15 +125,16 @@
   });
 
   test("hot reload doesn't reassemble if paused", () async {
-    final Completer<void> sawTick1 = Completer<void>();
+    await _flutter.run(withDebugger: true);
+    final Completer<void> sawTick2 = Completer<void>();
     final Completer<void> sawTick3 = Completer<void>();
     final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
     final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
     final StreamSubscription<String> subscription = _flutter.stdout.listen(
       (String line) {
-        if (line.contains('(((TICK 1)))')) {
-          expect(sawTick1.isCompleted, isFalse);
-          sawTick1.complete();
+        if (line.contains('((((TICK 2))))')) {
+          expect(sawTick2.isCompleted, isFalse);
+          sawTick2.complete();
         }
         if (line.contains('The application is paused in the debugger on a breakpoint.')) {
           expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
@@ -145,14 +146,13 @@
         }
       },
     );
-    await _flutter.run(withDebugger: true);
-    await sawTick1.future;
     await _flutter.addBreakpoint(
       _project.buildBreakpointUri,
       _project.buildBreakpointLine,
     );
     bool reloaded = false;
     final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
+    await sawTick2.future; // this should happen before it pauses
     final Isolate isolate = await _flutter.waitForPause();
     expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
     expect(reloaded, isFalse);