[flutter_tool] In 'attach' use platform dill and patched sdk dir from the Fuchsia SDK (#32071)

diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart
index 6a3e9d5..c822fcd 100644
--- a/packages/flutter_tools/lib/src/commands/attach.dart
+++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -6,7 +6,9 @@
 
 import 'package:multicast_dns/multicast_dns.dart';
 
+import '../artifacts.dart';
 import '../base/common.dart';
+import '../base/context.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/utils.dart';
@@ -160,8 +162,6 @@
 
   @override
   Future<FlutterCommandResult> runCommand() async {
-    final FlutterProject flutterProject = FlutterProject.current();
-
     Cache.releaseLockEarly();
 
     await _validateArguments();
@@ -169,6 +169,19 @@
     writePidFile(argResults['pid-file']);
 
     final Device device = await findTargetDevice();
+
+    final Artifacts artifacts = device.artifactOverrides ?? Artifacts.instance;
+    await context.run<void>(
+      body: () => _attachToDevice(device),
+      overrides: <Type, Generator>{
+        Artifacts: () => artifacts,
+    });
+
+    return null;
+  }
+
+  Future<void> _attachToDevice(Device device) async {
+    final FlutterProject flutterProject = FlutterProject.current();
     Future<int> getDevicePort() async {
       if (debugPort != null) {
         return debugPort;
@@ -310,7 +323,6 @@
         await device.portForwarder.unforward(port);
       }
     }
-    return null;
   }
 
   Future<void> _validateArguments() async { }
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 8765e54..fa0ae74 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -7,6 +7,7 @@
 
 import 'android/android_device.dart';
 import 'application_package.dart';
+import 'artifacts.dart';
 import 'base/context.dart';
 import 'base/file_system.dart';
 import 'base/utils.dart';
@@ -274,6 +275,9 @@
   /// Clear the device's logs.
   void clearLogs();
 
+  /// Optional device-specific artifact overrides.
+  OverrideArtifacts get artifactOverrides => null;
+
   /// Start an app package on the current device.
   ///
   /// [platformArgs] allows callers to pass platform-specific arguments to the
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index a2fddc2..f9fa27b 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -7,6 +7,7 @@
 import 'package:meta/meta.dart';
 
 import '../application_package.dart';
+import '../artifacts.dart';
 import '../base/common.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
@@ -224,6 +225,16 @@
   void clearLogs() {}
 
   @override
+  OverrideArtifacts get artifactOverrides {
+    return _artifactOverrides ??= OverrideArtifacts(
+      parent: Artifacts.instance,
+      platformKernelDill: fuchsiaArtifacts.platformKernelDill,
+      flutterPatchedSdk: fuchsiaArtifacts.flutterPatchedSdk,
+    );
+  }
+  OverrideArtifacts _artifactOverrides;
+
+  @override
   bool get supportsScreenshot => false;
 
   bool get ipv6 {
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
index b246a09..d7d3a51 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
@@ -81,7 +81,12 @@
 /// Fuchsia-specific artifacts used to interact with a device.
 class FuchsiaArtifacts {
   /// Creates a new [FuchsiaArtifacts].
-  FuchsiaArtifacts({this.sshConfig, this.devFinder});
+  FuchsiaArtifacts({
+    this.sshConfig,
+    this.devFinder,
+    this.platformKernelDill,
+    this.flutterPatchedSdk,
+  });
 
   /// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK.
   ///
@@ -92,6 +97,7 @@
   factory FuchsiaArtifacts.find() {
     final String fuchsia = Cache.instance.getArtifactDirectory('fuchsia').path;
     final String tools = fs.path.join(fuchsia, 'tools');
+    final String dartPrebuilts = fs.path.join(tools, 'dart_prebuilts');
 
     // If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir
     // relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it.
@@ -99,13 +105,17 @@
     File sshConfig;
     if (platform.environment.containsKey(_kFuchsiaBuildDir)) {
       sshConfig = fs.file(fs.path.join(
-          platform.environment[_kFuchsiaSshConfig], 'ssh-keys', 'ssh_config'));
+          platform.environment[_kFuchsiaBuildDir], 'ssh-keys', 'ssh_config'));
     } else if (platform.environment.containsKey(_kFuchsiaSshConfig)) {
       sshConfig = fs.file(platform.environment[_kFuchsiaSshConfig]);
     }
     return FuchsiaArtifacts(
       sshConfig: sshConfig,
       devFinder: fs.file(fs.path.join(tools, 'dev_finder')),
+      platformKernelDill: fs.file(fs.path.join(
+          dartPrebuilts, 'flutter_runner', 'platform_strong.dill')),
+      flutterPatchedSdk: fs.file(fs.path.join(
+          dartPrebuilts, 'flutter_runner')),
     );
   }
 
@@ -119,4 +129,10 @@
   /// The location of the dev finder tool used to locate connected
   /// Fuchsia devices.
   final File devFinder;
+
+  /// The location of the Fuchsia-specific platform dill.
+  final File platformKernelDill;
+
+  /// The directory containing [platformKernelDill].
+  final File flutterPatchedSdk;
 }
diff --git a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
index e7d3f51..0b5a727 100644
--- a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
+++ b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
@@ -10,7 +10,9 @@
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
 
+import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/common.dart';
+import 'package:flutter_tools/src/base/context.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/time.dart';
@@ -58,33 +60,98 @@
     });
   });
 
-  group('displays friendly error when', () {
-    final MockProcessManager mockProcessManager = MockProcessManager();
-    final MockProcessResult mockProcessResult = MockProcessResult();
-    final MockFile mockFile = MockFile();
-    when(mockProcessManager.run(
-      any,
-      environment: anyNamed('environment'),
-      workingDirectory: anyNamed('workingDirectory'),
-    )).thenAnswer((Invocation invocation) =>
-        Future<ProcessResult>.value(mockProcessResult));
-    when(mockProcessResult.exitCode).thenReturn(1);
-    when<String>(mockProcessResult.stdout).thenReturn('');
-    when<String>(mockProcessResult.stderr).thenReturn('');
-    when(mockFile.absolute).thenReturn(mockFile);
-    when(mockFile.path).thenReturn('');
+  group('Fuchsia device artifact overrides', () {
+    MockFile devFinder;
+    MockFile sshConfig;
+    MockFile platformDill;
+    MockFile patchedSdk;
 
-    final MockProcessManager emptyStdoutProcessManager = MockProcessManager();
-    final MockProcessResult emptyStdoutProcessResult = MockProcessResult();
-    when(emptyStdoutProcessManager.run(
-      any,
-      environment: anyNamed('environment'),
-      workingDirectory: anyNamed('workingDirectory'),
-    )).thenAnswer((Invocation invocation) =>
-        Future<ProcessResult>.value(emptyStdoutProcessResult));
-    when(emptyStdoutProcessResult.exitCode).thenReturn(0);
-    when<String>(emptyStdoutProcessResult.stdout).thenReturn('');
-    when<String>(emptyStdoutProcessResult.stderr).thenReturn('');
+    setUp(() {
+      devFinder = MockFile();
+      sshConfig = MockFile();
+      platformDill = MockFile();
+      patchedSdk = MockFile();
+      when(devFinder.absolute).thenReturn(devFinder);
+      when(sshConfig.absolute).thenReturn(sshConfig);
+      when(platformDill.absolute).thenReturn(platformDill);
+      when(patchedSdk.absolute).thenReturn(patchedSdk);
+    });
+
+    testUsingContext('exist', () async {
+      final FuchsiaDevice device = FuchsiaDevice('fuchsia-device');
+      expect(device.artifactOverrides, isNotNull);
+      expect(device.artifactOverrides.platformKernelDill, equals(platformDill));
+      expect(device.artifactOverrides.flutterPatchedSdk, equals(patchedSdk));
+    }, overrides: <Type, Generator>{
+      FuchsiaArtifacts: () => FuchsiaArtifacts(
+            sshConfig: sshConfig,
+            devFinder: devFinder,
+            platformKernelDill: platformDill,
+            flutterPatchedSdk: patchedSdk,
+          ),
+    });
+
+    testUsingContext('are used', () async {
+      final FuchsiaDevice device = FuchsiaDevice('fuchsia-device');
+      expect(device.artifactOverrides, isNotNull);
+      expect(device.artifactOverrides.platformKernelDill, equals(platformDill));
+      expect(device.artifactOverrides.flutterPatchedSdk, equals(patchedSdk));
+      await context.run<void>(
+        body: () {
+          expect(Artifacts.instance.getArtifactPath(Artifact.platformKernelDill),
+                 equals(platformDill.path));
+          expect(Artifacts.instance.getArtifactPath(Artifact.flutterPatchedSdkPath),
+                 equals(patchedSdk.path));
+        },
+        overrides: <Type, Generator>{
+          Artifacts: () => device.artifactOverrides,
+        },
+      );
+    }, overrides: <Type, Generator>{
+      FuchsiaArtifacts: () => FuchsiaArtifacts(
+            sshConfig: sshConfig,
+            devFinder: devFinder,
+            platformKernelDill: platformDill,
+            flutterPatchedSdk: patchedSdk,
+          ),
+    });
+  });
+
+  group('displays friendly error when', () {
+    MockProcessManager mockProcessManager;
+    MockProcessResult mockProcessResult;
+    MockFile mockFile;
+    MockProcessManager emptyStdoutProcessManager;
+    MockProcessResult emptyStdoutProcessResult;
+
+    setUp(() {
+      mockProcessManager = MockProcessManager();
+      mockProcessResult = MockProcessResult();
+      mockFile = MockFile();
+      when(mockProcessManager.run(
+        any,
+        environment: anyNamed('environment'),
+        workingDirectory: anyNamed('workingDirectory'),
+      )).thenAnswer((Invocation invocation) =>
+          Future<ProcessResult>.value(mockProcessResult));
+      when(mockProcessResult.exitCode).thenReturn(1);
+      when<String>(mockProcessResult.stdout).thenReturn('');
+      when<String>(mockProcessResult.stderr).thenReturn('');
+      when(mockFile.absolute).thenReturn(mockFile);
+      when(mockFile.path).thenReturn('');
+
+      emptyStdoutProcessManager = MockProcessManager();
+      emptyStdoutProcessResult = MockProcessResult();
+      when(emptyStdoutProcessManager.run(
+        any,
+        environment: anyNamed('environment'),
+        workingDirectory: anyNamed('workingDirectory'),
+      )).thenAnswer((Invocation invocation) =>
+          Future<ProcessResult>.value(emptyStdoutProcessResult));
+      when(emptyStdoutProcessResult.exitCode).thenReturn(0);
+      when<String>(emptyStdoutProcessResult.stdout).thenReturn('');
+      when<String>(emptyStdoutProcessResult.stderr).thenReturn('');
+    });
 
     testUsingContext('No vmservices found', () async {
       final FuchsiaDevice device = FuchsiaDevice('id');
@@ -116,26 +183,29 @@
 [2018-11-09 01:30:12][52580][52983][log] INFO: example_app.cmx(flutter): Did thing this time
 
   ''';
-      final MockProcessManager mockProcessManager = MockProcessManager();
-      final MockProcess mockProcess = MockProcess();
+      MockProcessManager mockProcessManager;
+      MockProcess mockProcess;
       Completer<int> exitCode;
       StreamController<List<int>> stdout;
       StreamController<List<int>> stderr;
-      when(mockProcessManager.start(any))
-          .thenAnswer((Invocation _) => Future<Process>.value(mockProcess));
-      when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future);
-      when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream);
-      when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream);
-
-      final MockFile devFinder = MockFile();
-      final MockFile sshConfig = MockFile();
-      when(devFinder.absolute).thenReturn(devFinder);
-      when(sshConfig.absolute).thenReturn(sshConfig);
+      MockFile devFinder;
+      MockFile sshConfig;
 
       setUp(() {
+        mockProcessManager = MockProcessManager();
+        mockProcess = MockProcess();
         stdout = StreamController<List<int>>(sync: true);
         stderr = StreamController<List<int>>(sync: true);
         exitCode = Completer<int>();
+        when(mockProcessManager.start(any))
+            .thenAnswer((Invocation _) => Future<Process>.value(mockProcess));
+        when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future);
+        when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream);
+        when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream);
+        devFinder = MockFile();
+        sshConfig = MockFile();
+        when(devFinder.absolute).thenReturn(devFinder);
+        when(sshConfig.absolute).thenReturn(sshConfig);
       });
 
       tearDown(() {