[flutter_tools] Reland initialize frontend_sever with build (#51477)

diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
index 606d5b2..469f73e 100644
--- a/packages/flutter_tools/lib/src/compile.dart
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -469,7 +469,6 @@
   // 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 3400c82..b985bff 100644
--- a/packages/flutter_tools/lib/src/devfs.dart
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -488,6 +488,9 @@
     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 b58487e..d24e3c7 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -629,16 +629,6 @@
     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 67a93d3..32f98a6 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -16,6 +16,7 @@
 import 'base/logger.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
+import 'bundle.dart';
 import 'compile.dart';
 import 'convert.dart';
 import 'devfs.dart';
@@ -329,14 +330,36 @@
 
     firstBuildTime = DateTime.now();
 
+    final List<Future<bool>> startupTasks = <Future<bool>>[];
     for (final FlutterDevice device in flutterDevices) {
-      final int result = await device.runHot(
+      // 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(
         hotRunner: this,
         route: route,
-      );
-      if (result != 0) {
-        return result;
+      ).then((int result) => result == 0));
+    }
+    try {
+      final List<bool> results = await Future.wait(startupTasks);
+      if (!results.every((bool passed) => passed)) {
+        return 1;
       }
+    } 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 77f60ad..9a9535e 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -84,47 +84,6 @@
       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 f443115..d570b08 100644
--- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
@@ -234,10 +234,6 @@
     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 455e756..612de2f 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 {
-    await _flutter.run();
-    _project.uncommentHotReloadPrint();
     final StringBuffer stdout = StringBuffer();
     final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
+    await _flutter.run();
+    _project.uncommentHotReloadPrint();
     try {
       await _flutter.hotReload();
       expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
@@ -49,10 +49,10 @@
   });
 
   test('reloadMethod triggers hot reload behavior', () async {
-    await _flutter.run();
-    _project.uncommentHotReloadPrint();
     final StringBuffer stdout = StringBuffer();
     final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
+    await _flutter.run();
+    _project.uncommentHotReloadPrint();
     try {
       final String libraryId = _project.buildBreakpointUri.toString();
       await _flutter.reloadMethod(libraryId: libraryId, classId: 'MyApp');
@@ -72,7 +72,6 @@
 
   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>();
@@ -92,6 +91,7 @@
         }
       },
     );
+    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,16 +125,15 @@
   });
 
   test("hot reload doesn't reassemble if paused", () async {
-    await _flutter.run(withDebugger: true);
-    final Completer<void> sawTick2 = Completer<void>();
+    final Completer<void> sawTick1 = 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 2))))')) {
-          expect(sawTick2.isCompleted, isFalse);
-          sawTick2.complete();
+        if (line.contains('(((TICK 1)))')) {
+          expect(sawTick1.isCompleted, isFalse);
+          sawTick1.complete();
         }
         if (line.contains('The application is paused in the debugger on a breakpoint.')) {
           expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
@@ -146,13 +145,14 @@
         }
       },
     );
+    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);