refactor depfile usage and update linux rule (#42487)

diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart
index cba474a..bb7d035 100644
--- a/packages/flutter_tools/lib/src/build_system/build_system.dart
+++ b/packages/flutter_tools/lib/src/build_system/build_system.dart
@@ -502,7 +502,7 @@
         outputFiles[output.path] = output;
       }
       for (File input in node.inputs) {
-        final String resolvedPath = input.resolveSymbolicLinksSync();
+        final String resolvedPath = input.absolute.path;
         if (outputFiles.containsKey(resolvedPath)) {
           continue;
         }
@@ -704,7 +704,7 @@
   /// One or more reasons why a task was invalidated.
   ///
   /// May be empty if the task was skipped.
-  final Set<InvalidedReason> invalidatedReasons = <InvalidedReason>{};
+  final Set<InvalidatedReason> invalidatedReasons = <InvalidatedReason>{};
 
   /// Whether this node needs an action performed.
   bool get dirty => _dirty;
@@ -735,7 +735,7 @@
       if (fileHashStore.currentHashes.containsKey(absolutePath)) {
         final String currentHash = fileHashStore.currentHashes[absolutePath];
         if (currentHash != previousHash) {
-          invalidatedReasons.add(InvalidedReason.inputChanged);
+          invalidatedReasons.add(InvalidatedReason.inputChanged);
           _dirty = true;
         }
       } else {
@@ -749,13 +749,13 @@
       // output paths changed.
       if (!currentOutputPaths.contains(previousOutput)) {
         _dirty = true;
-        invalidatedReasons.add(InvalidedReason.outputSetChanged);
+        invalidatedReasons.add(InvalidatedReason.outputSetChanged);
         // if this isn't a current output file there is no reason to compute the hash.
         continue;
       }
       final File file = fs.file(previousOutput);
       if (!file.existsSync()) {
-        invalidatedReasons.add(InvalidedReason.outputMissing);
+        invalidatedReasons.add(InvalidatedReason.outputMissing);
         _dirty = true;
         continue;
       }
@@ -764,7 +764,7 @@
       if (fileHashStore.currentHashes.containsKey(absolutePath)) {
         final String currentHash = fileHashStore.currentHashes[absolutePath];
         if (currentHash != previousHash) {
-          invalidatedReasons.add(InvalidedReason.outputChanged);
+          invalidatedReasons.add(InvalidatedReason.outputChanged);
           _dirty = true;
         }
       } else {
@@ -772,9 +772,14 @@
       }
     }
 
-    // If we depend on a file that doesnt exist on disk, kill the build.
+    // If we depend on a file that doesnt exist on disk, mark the build as
+    // dirty. if the rule is not correctly specified, this will result in it
+    // always being rerun.
     if (missingInputs.isNotEmpty) {
-      throw MissingInputException(missingInputs, target.name);
+      _dirty = true;
+      final String missingMessage = missingInputs.map((File file) => file.path).join(', ');
+      printTrace('invalidated build due to missing files: $missingMessage');
+      invalidatedReasons.add(InvalidatedReason.inputMissing);
     }
 
     // If we have files to hash, compute them asynchronously and then
@@ -782,7 +787,7 @@
     if (sourcesToHash.isNotEmpty) {
       final List<File> dirty = await fileHashStore.hashFiles(sourcesToHash);
       if (dirty.isNotEmpty) {
-        invalidatedReasons.add(InvalidedReason.inputChanged);
+        invalidatedReasons.add(InvalidatedReason.inputChanged);
         _dirty = true;
       }
     }
@@ -790,8 +795,12 @@
   }
 }
 
-/// A description of why a task was rerun.
-enum InvalidedReason {
+/// A description of why a target was rerun.
+enum InvalidatedReason {
+  /// An input file that was expected is missing. This can occur when using
+  /// depfile dependencies, or if a target is incorrectly specified.
+  inputMissing,
+
   /// An input file has an updated hash.
   inputChanged,
 
diff --git a/packages/flutter_tools/lib/src/build_system/depfile.dart b/packages/flutter_tools/lib/src/build_system/depfile.dart
new file mode 100644
index 0000000..1df5e7d
--- /dev/null
+++ b/packages/flutter_tools/lib/src/build_system/depfile.dart
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../base/file_system.dart';
+import '../base/platform.dart';
+import '../globals.dart';
+
+/// A class for representing depfile formats.
+class Depfile {
+  /// Create a [Depfile] from a list of [input] files and [output] files.
+  const Depfile(this.inputs, this.outputs);
+
+  /// Parse the depfile contents from [file].
+  ///
+  /// If the syntax is invalid, returns an empty [Depfile].
+  factory Depfile.parse(File file) {
+    final String contents = file.readAsStringSync();
+    final List<String> colonSeparated = contents.split(': ');
+    if (colonSeparated.length != 2) {
+      printError('Invalid depfile: ${file.path}');
+      return const Depfile(<File>[], <File>[]);
+    }
+    final List<File> inputs = _processList(colonSeparated[1].trim());
+    final List<File> outputs = _processList(colonSeparated[0].trim());
+    return Depfile(inputs, outputs);
+  }
+
+  /// The input files for this depfile.
+  final List<File> inputs;
+
+  /// The output files for this depfile.
+  final List<File> outputs;
+
+  /// Given an [depfile] File, write the depfile contents.
+  ///
+  /// If either [inputs] or [outputs] is empty, does not write to the file.
+  void writeToFile(File depfile) {
+    if (inputs.isEmpty || outputs.isEmpty) {
+      return;
+    }
+    final StringBuffer buffer = StringBuffer();
+    _writeFilesToBuffer(outputs, buffer);
+    buffer.write(': ');
+    _writeFilesToBuffer(inputs, buffer);
+    depfile.writeAsStringSync(buffer.toString());
+  }
+
+  void _writeFilesToBuffer(List<File> files, StringBuffer buffer) {
+    for (File outputFile in files) {
+      if (platform.isWindows) {
+        // Paths in a depfile have to be escaped on windows.
+        final String escapedPath = outputFile.path.replaceAll(r'\', r'\\');
+        buffer.write(' $escapedPath');
+      } else {
+        buffer.write(' ${outputFile.path}');
+      }
+    }
+  }
+
+  static final RegExp _separatorExpr = RegExp(r'([^\\]) ');
+  static final RegExp _escapeExpr = RegExp(r'\\(.)');
+
+  static List<File> _processList(String rawText) {
+    return rawText
+    // Put every file on right-hand side on the separate line
+        .replaceAllMapped(_separatorExpr, (Match match) => '${match.group(1)}\n')
+        .split('\n')
+    // Expand escape sequences, so that '\ ', for example,ß becomes ' '
+        .map<String>((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim())
+        .where((String path) => path.isNotEmpty)
+    // The tool doesn't write duplicates to these lists. This call is an attempt to
+    // be resillient to the outputs of other tools which write or user edits to depfiles.
+        .toSet()
+        .map((String path) => fs.file(path))
+        .toList();
+  }
+}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/linux.dart b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
index 4821163..ab9abc9 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/linux.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
@@ -11,10 +11,22 @@
 import '../../devfs.dart';
 import '../../globals.dart';
 import '../build_system.dart';
+import '../depfile.dart';
 import '../exceptions.dart';
 import 'assets.dart';
 import 'dart.dart';
 
+/// The only files/subdirectories we care out.
+const List<String> _kLinuxArtifacts = <String>[
+  'libflutter_linux_glfw.so',
+  'flutter_export.h',
+  'flutter_messenger.h',
+  'flutter_plugin_registrar.h',
+  'flutter_glfw.h',
+  'icudtl.dat',
+  'cpp_client_wrapper_glfw/',
+];
+
 /// Copies the Linux desktop embedding files to the copy directory.
 class UnpackLinuxDebug extends Target {
   const UnpackLinuxDebug();
@@ -25,17 +37,12 @@
   @override
   List<Source> get inputs => const <Source>[
     Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
-    Source.artifact(Artifact.linuxDesktopPath, mode: BuildMode.debug),
+    Source.depfile('linux_engine_sources.d'),
   ];
 
   @override
   List<Source> get outputs => const <Source>[
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/libflutter_linux_glfw.so'),
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_export.h'),
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_messenger.h'),
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_plugin_registrar.h'),
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_glfw.h'),
-    Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/icudtl.dat'),
+    Source.depfile('linux_engine_sources.d'),
   ];
 
   @override
@@ -44,22 +51,55 @@
   @override
   Future<void> build(Environment environment) async {
     final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
-    for (File input in fs.directory(basePath)
-        .listSync(recursive: true)
-        .whereType<File>()) {
-      final String outputPath = fs.path.join(
-        environment.projectDir.path,
-        'linux',
-        'flutter',
-        'ephemeral',
-        fs.path.relative(input.path, from: basePath),
-      );
-      final File destinationFile = fs.file(outputPath);
-      if (!destinationFile.parent.existsSync()) {
-        destinationFile.parent.createSync(recursive: true);
+    final List<File> inputs = <File>[];
+    final List<File> outputs = <File>[];
+    final String outputPrefix = fs.path.join(
+      environment.projectDir.path,
+      'linux',
+      'flutter',
+      'ephemeral',
+    );
+    // The native linux artifacts are composed of 6 files and a directory (listed above)
+    // which need to be copied to the target directory.
+    for (String artifact in _kLinuxArtifacts) {
+      final String entityPath = fs.path.join(basePath, artifact);
+      // If this artifact is a file, just copy the source over.
+      if (fs.isFileSync(entityPath)) {
+        final String outputPath = fs.path.join(
+          outputPrefix,
+          fs.path.relative(entityPath, from: basePath),
+        );
+        final File destinationFile = fs.file(outputPath);
+        if (!destinationFile.parent.existsSync()) {
+          destinationFile.parent.createSync(recursive: true);
+        }
+        final File inputFile = fs.file(entityPath);
+        inputFile.copySync(destinationFile.path);
+        inputs.add(inputFile);
+        outputs.add(destinationFile);
+        continue;
       }
-      fs.file(input).copySync(destinationFile.path);
+      // If the artifact is the directory cpp_client_wrapper, recursively
+      // copy every file from it.
+      for (File input in fs.directory(entityPath)
+          .listSync(recursive: true)
+          .whereType<File>()) {
+        final String outputPath = fs.path.join(
+          outputPrefix,
+          fs.path.relative(input.path, from: basePath),
+        );
+        final File destinationFile = fs.file(outputPath);
+        if (!destinationFile.parent.existsSync()) {
+          destinationFile.parent.createSync(recursive: true);
+        }
+        final File inputFile = fs.file(input);
+        inputFile.copySync(destinationFile.path);
+        inputs.add(inputFile);
+        outputs.add(destinationFile);
+      }
     }
+    final Depfile depfile = Depfile(inputs, outputs);
+    depfile.writeToFile(environment.buildDir.childFile('linux_engine_sources.d'));
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart
index 65cd920..996b166 100644
--- a/packages/flutter_tools/lib/src/bundle.dart
+++ b/packages/flutter_tools/lib/src/bundle.dart
@@ -7,15 +7,13 @@
 import 'package:meta/meta.dart';
 import 'package:pool/pool.dart';
 
-import 'artifacts.dart';
 import 'asset.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
-import 'base/platform.dart';
 import 'build_info.dart';
 import 'build_system/build_system.dart';
+import 'build_system/depfile.dart';
 import 'build_system/targets/dart.dart';
-import 'compile.dart';
 import 'dart/package_map.dart';
 import 'devfs.dart';
 import 'globals.dart';
@@ -45,10 +43,6 @@
 
 const String defaultPrivateKeyPath = 'privatekey.der';
 
-const String _kKernelKey = 'kernel_blob.bin';
-const String _kVMSnapshotData = 'vm_snapshot_data';
-const String _kIsolateSnapshotData = 'isolate_snapshot_data';
-
 /// Provides a `build` method that builds the bundle.
 class BundleBuilder {
   /// Builds the bundle for the given target platform.
@@ -72,73 +66,29 @@
     List<String> extraGenSnapshotOptions = const <String>[],
     List<String> fileSystemRoots,
     String fileSystemScheme,
-    bool shouldBuildWithAssemble = false,
   }) async {
     mainPath ??= defaultMainPath;
     depfilePath ??= defaultDepfilePath;
     assetDirPath ??= getAssetBuildDirectory();
     packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
-    applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
     final FlutterProject flutterProject = FlutterProject.current();
-
-    if (shouldBuildWithAssemble) {
-      await buildWithAssemble(
-        buildMode: buildMode ?? BuildMode.debug,
-        targetPlatform: platform,
-        mainPath: mainPath,
-        flutterProject: flutterProject,
-        outputDir: assetDirPath,
-        depfilePath: depfilePath,
-        precompiled: precompiledSnapshot,
-      );
-      return;
-    }
-
-    DevFSContent kernelContent;
-    if (!precompiledSnapshot) {
-      if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) {
-        printTrace('Extra front-end options: $extraFrontEndOptions');
-      }
-      ensureDirectoryExists(applicationKernelFilePath);
-      final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
-      final CompilerOutput compilerOutput = await kernelCompiler.compile(
-        sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
-        mainPath: fs.file(mainPath).absolute.path,
-        outputFilePath: applicationKernelFilePath,
-        depFilePath: depfilePath,
-        buildMode: buildMode,
-        trackWidgetCreation: trackWidgetCreation,
-        extraFrontEndOptions: extraFrontEndOptions,
-        fileSystemRoots: fileSystemRoots,
-        fileSystemScheme: fileSystemScheme,
-        packagesPath: packagesPath,
-      );
-      if (compilerOutput?.outputFilename == null) {
-        throwToolExit('Compiler failed on $mainPath');
-      }
-      kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
-
-      fs.directory(getBuildDirectory()).childFile('frontend_server.d')
-          .writeAsStringSync('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
-    }
-
-    final AssetBundle assets = await buildAssets(
-      manifestPath: manifestPath,
-      assetDirPath: assetDirPath,
-      packagesPath: packagesPath,
-      reportLicensedPackages: reportLicensedPackages,
+    await buildWithAssemble(
+      buildMode: buildMode ?? BuildMode.debug,
+      targetPlatform: platform,
+      mainPath: mainPath,
+      flutterProject: flutterProject,
+      outputDir: assetDirPath,
+      depfilePath: depfilePath,
+      precompiled: precompiledSnapshot,
     );
-    if (assets == null) {
-      throwToolExit('Error building assets', exitCode: 1);
+    // Work around for flutter_tester placing kernel artifacts in odd places.
+    if (applicationKernelFilePath != null) {
+      final File outputDill = fs.directory(assetDirPath).childFile('kernel_blob.bin');
+      if (outputDill.existsSync()) {
+        outputDill.copySync(applicationKernelFilePath);
+      }
     }
-
-    await assemble(
-      buildMode: buildMode,
-      assetBundle: assets,
-      kernelContent: kernelContent,
-      privateKeyPath: privateKeyPath,
-      assetDirPath: assetDirPath,
-    );
+    return;
   }
 }
 
@@ -178,30 +128,13 @@
     }
     throwToolExit('Failed to build bundle.');
   }
-
-  // Output depfile format:
-  final StringBuffer buffer = StringBuffer();
-  buffer.write('flutter_bundle');
-  _writeFilesToBuffer(result.outputFiles, buffer);
-  buffer.write(': ');
-  _writeFilesToBuffer(result.inputFiles, buffer);
-
-  final File depfile = fs.file(depfilePath);
-  if (!depfile.parent.existsSync()) {
-    depfile.parent.createSync(recursive: true);
-  }
-  depfile.writeAsStringSync(buffer.toString());
-}
-
-void _writeFilesToBuffer(List<File> files, StringBuffer buffer) {
-  for (File outputFile in files) {
-    if (platform.isWindows) {
-      // Paths in a depfile have to be escaped on windows.
-      final String escapedPath = outputFile.path.replaceAll(r'\', r'\\');
-      buffer.write(' $escapedPath');
-    } else {
-      buffer.write(' ${outputFile.path}');
+  if (depfilePath != null) {
+    final Depfile depfile = Depfile(result.inputFiles, result.outputFiles);
+    final File outputDepfile = fs.file(depfilePath);
+    if (!outputDepfile.parent.existsSync()) {
+      outputDepfile.parent.createSync(recursive: true);
     }
+    depfile.writeToFile(outputDepfile);
   }
 }
 
@@ -231,32 +164,6 @@
   return assetBundle;
 }
 
-Future<void> assemble({
-  BuildMode buildMode,
-  AssetBundle assetBundle,
-  DevFSContent kernelContent,
-  String privateKeyPath = defaultPrivateKeyPath,
-  String assetDirPath,
-}) async {
-  assetDirPath ??= getAssetBuildDirectory();
-  printTrace('Building bundle');
-
-  final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
-  if (kernelContent != null) {
-    final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode);
-    final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: buildMode);
-    assetEntries[_kKernelKey] = kernelContent;
-    assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
-    assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
-  }
-
-  printTrace('Writing asset files to $assetDirPath');
-  ensureDirectoryExists(assetDirPath);
-
-  await writeBundle(fs.directory(assetDirPath), assetEntries);
-  printTrace('Wrote $assetDirPath');
-}
-
 Future<void> writeBundle(
   Directory bundleDir,
   Map<String, DevFSContent> assetEntries,
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index a65803a..72e9be5 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -138,7 +138,6 @@
       extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
       fileSystemScheme: argResults['filesystem-scheme'],
       fileSystemRoots: argResults['filesystem-root'],
-      shouldBuildWithAssemble: true,
     );
     return null;
   }
diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart
index 9deadc0..75844c9 100644
--- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart
+++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart
@@ -145,12 +145,13 @@
       trackWidgetCreation: buildInfo.trackWidgetCreation,
     );
     await BundleBuilder().build(
+      buildMode: buildInfo.mode,
       mainPath: mainPath,
       assetDirPath: assetDirPath,
       applicationKernelFilePath: applicationKernelFilePath,
       precompiledSnapshot: false,
-      buildMode: buildInfo.mode,
       trackWidgetCreation: buildInfo.trackWidgetCreation,
+      platform: getTargetPlatformForName(getNameForHostPlatform(getCurrentHostPlatform())),
     );
     command.add('--flutter-assets-dir=$assetDirPath');
 
diff --git a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
index 809ea03..b782682 100644
--- a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
@@ -106,13 +106,12 @@
     );
   });
 
-  test('Throws exception if asked to build with missing inputs', () => testbed.run(() async {
+  test('Does not throw exception if asked to build with missing inputs', () => testbed.run(() async {
     // Delete required input file.
     fs.file('foo.dart').deleteSync();
     final BuildResult buildResult = await buildSystem.build(fooTarget, environment);
 
-    expect(buildResult.hasException, true);
-    expect(buildResult.exceptions.values.single.exception, isInstanceOf<MissingInputException>());
+    expect(buildResult.hasException, false);
   }));
 
   test('Throws exception if it does not produce a specified output', () => testbed.run(() async {
diff --git a/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart
new file mode 100644
index 0000000..aa0fd38
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:file/memory.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/build_system/depfile.dart';
+
+import '../../src/common.dart';
+import '../../src/testbed.dart';
+
+void main() {
+  Testbed testbed;
+
+  setUp(() {
+    testbed = Testbed();
+  });
+  test('Can parse depfile from file', () => testbed.run(() {
+    final File depfileSource = fs.file('example.d')..writeAsStringSync('''
+a.txt: b.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs.single.path, 'b.txt');
+    expect(depfile.outputs.single.path, 'a.txt');
+  }));
+
+  test('Can parse depfile with multiple inputs', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync('''
+a.txt: b.txt c.txt d.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs.map((File file) => file.path), <String>[
+      'b.txt',
+      'c.txt',
+      'd.txt',
+    ]);
+    expect(depfile.outputs.single.path, 'a.txt');
+  }));
+
+  test('Can parse depfile with multiple outputs', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync('''
+a.txt c.txt d.txt: b.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs.single.path, 'b.txt');
+    expect(depfile.outputs.map((File file) => file.path), <String>[
+      'a.txt',
+      'c.txt',
+      'd.txt',
+    ]);
+  }));
+
+  test('Can parse depfile with windows file paths', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
+C:\\a.txt: C:\\b.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs.single.path, r'C:\b.txt');
+    expect(depfile.outputs.single.path, r'C:\a.txt');
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
+  }));
+
+  test('Resillient to weird whitespace', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
+a.txt
+  : b.txt    c.txt
+
+
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs, hasLength(2));
+    expect(depfile.outputs.single.path, 'a.txt');
+  }));
+
+  test('Resillient to duplicate files', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
+a.txt: b.txt b.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs.single.path, 'b.txt');
+    expect(depfile.outputs.single.path, 'a.txt');
+  }));
+
+  test('Resillient to malformed file, missing :', () => testbed.run(() {
+    final FileSystem fs = MemoryFileSystem();
+    final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
+a.text b.txt
+''');
+    final Depfile depfile = Depfile.parse(depfileSource);
+
+    expect(depfile.inputs, isEmpty);
+    expect(depfile.outputs, isEmpty);
+  }));
+}
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
index d707235..f4bb3dd 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
@@ -39,6 +39,7 @@
           kBuildMode: 'debug',
         }
       );
+      fs.file('bin/cache/artifacts/engine/linux-x64/unrelated-stuff').createSync(recursive: true);
       fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux_glfw.so').createSync(recursive: true);
       fs.file('bin/cache/artifacts/engine/linux-x64/flutter_export.h').createSync();
       fs.file('bin/cache/artifacts/engine/linux-x64/flutter_messenger.h').createSync();
@@ -53,7 +54,7 @@
     });
   });
 
-  test('Copies files to correct cache directory', () => testbed.run(() async {
+  test('Copies files to correct cache directory, excluding unrelated code', () => testbed.run(() async {
     final BuildResult result = await buildSystem.build(const UnpackLinuxDebug(), environment);
 
     expect(result.hasException, false);
@@ -64,6 +65,7 @@
     expect(fs.file('linux/flutter/ephemeral/flutter_glfw.h').existsSync(), true);
     expect(fs.file('linux/flutter/ephemeral/icudtl.dat').existsSync(), true);
     expect(fs.file('linux/flutter/ephemeral/cpp_client_wrapper_glfw/foo').existsSync(), true);
+    expect(fs.file('linux/flutter/ephemeral/unrelated-stuff').existsSync(), false);
   }));
 
   test('Does not re-copy files unecessarily', () => testbed.run(() async {
diff --git a/packages/flutter_tools/test/general.shard/bundle_shim_test.dart b/packages/flutter_tools/test/general.shard/bundle_shim_test.dart
index be36d51..6b678c1 100644
--- a/packages/flutter_tools/test/general.shard/bundle_shim_test.dart
+++ b/packages/flutter_tools/test/general.shard/bundle_shim_test.dart
@@ -43,7 +43,7 @@
     );
     expect(fs.file(fs.path.join('example', 'kernel_blob.bin')).existsSync(), true);
     expect(fs.file(fs.path.join('example', 'LICENSE')).existsSync(), true);
-    expect(fs.file(fs.path.join('example.d')).existsSync(), true);
+    expect(fs.file(fs.path.join('example.d')).existsSync(), false);
   }));
 
   test('Handles build system failure', () => testbed.run(() {
diff --git a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart
index f782d78..fa6a4f0 100644
--- a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart
+++ b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart
@@ -9,10 +9,8 @@
 import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/build_info.dart';
-import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/compile.dart';
+import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/device.dart';
-import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/tester/flutter_tester.dart';
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
@@ -102,24 +100,23 @@
       String mainPath;
 
       MockArtifacts mockArtifacts;
-      MockKernelCompiler mockKernelCompiler;
       MockProcessManager mockProcessManager;
       MockProcess mockProcess;
+      MockBuildSystem mockBuildSystem;
 
       final Map<Type, Generator> startOverrides = <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: 'linux'),
         FileSystem: () => fs,
         ProcessManager: () => mockProcessManager,
-        Cache: () => Cache(rootOverride: fs.directory(flutterRoot)),
-        KernelCompilerFactory: () => FakeKernelCompilerFactory(mockKernelCompiler),
         Artifacts: () => mockArtifacts,
+        BuildSystem: () => mockBuildSystem,
       };
 
       setUp(() {
+        mockBuildSystem = MockBuildSystem();
         flutterRoot = fs.path.join('home', 'me', 'flutter');
         flutterTesterPath = fs.path.join(flutterRoot, 'bin', 'cache',
             'artifacts', 'engine', 'linux-x64', 'flutter_tester');
-
         final File flutterTesterFile = fs.file(flutterTesterPath);
         flutterTesterFile.parent.createSync(recursive: true);
         flutterTesterFile.writeAsBytesSync(const <int>[]);
@@ -139,24 +136,23 @@
           mode: anyNamed('mode')
         )).thenReturn(artifactPath);
 
-        mockKernelCompiler = MockKernelCompiler();
+        when(mockBuildSystem.build(
+          any,
+          any,
+        )).thenAnswer((_) async {
+          fs.file('$mainPath.dill').createSync(recursive: true);
+          return BuildResult(success: true);
+        });
       });
 
       testUsingContext('not debug', () async {
         final LaunchResult result = await device.startApp(null,
             mainPath: mainPath,
             debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null)));
+
         expect(result.started, isFalse);
       }, overrides: startOverrides);
 
-      testUsingContext('no flutter_tester', () async {
-        fs.file(flutterTesterPath).deleteSync();
-        expect(() async {
-          await device.startApp(null,
-              mainPath: mainPath,
-              debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.debug, null)));
-        }, throwsToolExit());
-      }, overrides: startOverrides);
 
       testUsingContext('start', () async {
         final Uri observatoryUri = Uri.parse('http://127.0.0.1:6666/');
@@ -168,22 +164,6 @@
               .codeUnits,
         ]));
 
-        when(mockKernelCompiler.compile(
-          sdkRoot: anyNamed('sdkRoot'),
-          mainPath: anyNamed('mainPath'),
-          outputFilePath: anyNamed('outputFilePath'),
-          depFilePath: anyNamed('depFilePath'),
-          buildMode: BuildMode.debug,
-          trackWidgetCreation: anyNamed('trackWidgetCreation'),
-          extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-          fileSystemRoots: anyNamed('fileSystemRoots'),
-          fileSystemScheme: anyNamed('fileSystemScheme'),
-          packagesPath: anyNamed('packagesPath'),
-        )).thenAnswer((_) async {
-          fs.file('$mainPath.dill').createSync(recursive: true);
-          return CompilerOutput('$mainPath.dill', 0, <Uri>[]);
-        });
-
         final LaunchResult result = await device.startApp(null,
             mainPath: mainPath,
             debuggingOptions: DebuggingOptions.enabled(const BuildInfo(BuildMode.debug, null)));
@@ -195,15 +175,5 @@
   });
 }
 
+class MockBuildSystem extends Mock implements BuildSystem {}
 class MockArtifacts extends Mock implements Artifacts {}
-class MockKernelCompiler extends Mock implements KernelCompiler {}
-class FakeKernelCompilerFactory implements KernelCompilerFactory {
-  FakeKernelCompilerFactory(this.kernelCompiler);
-
-  final KernelCompiler kernelCompiler;
-
-  @override
-  Future<KernelCompiler> create(FlutterProject flutterProject) async {
-    return kernelCompiler;
-  }
-}