Enable Hot Reload on Windows (backed by gen_snapshot) (#8512)

* Enable Hot Reload on Windows (backed by gen_snapshot)

\o/

Two caveats:
* Hot Reload on Windows is slower than on other platforms because gen_snapshot is slower then sky_snapshot
* We currently cannot hot reload projects with spaces in the path

* enable tests
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index c361d4b..8f837a0 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -258,6 +258,7 @@
           : new IOSApp.fromIpa(applicationBinary);
     case TargetPlatform.darwin_x64:
     case TargetPlatform.linux_x64:
+    case TargetPlatform.windows_x64:
       return null;
   }
   assert(platform != null);
@@ -282,6 +283,7 @@
         return iOS;
       case TargetPlatform.darwin_x64:
       case TargetPlatform.linux_x64:
+      case TargetPlatform.windows_x64:
         return null;
     }
     return null;
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 0aa9ddc..2655019 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -21,6 +21,8 @@
   skySnapshot,
   snapshotDart,
   flutterFramework,
+  vmSnapshotData,
+  isolateSnapshotData
 }
 
 String _artifactToFileName(Artifact artifact) {
@@ -49,6 +51,10 @@
       return 'snapshot.dart';
     case Artifact.flutterFramework:
       return 'Flutter.framework';
+    case Artifact.vmSnapshotData:
+      return 'vm_isolate_snapshot.bin';
+    case Artifact.isolateSnapshotData:
+      return 'isolate_snapshot.bin';
   }
   assert(false, 'Invalid artifact $artifact.');
   return null;
@@ -85,6 +91,7 @@
         return _getIosArtifactPath(artifact, platform, mode);
       case TargetPlatform.darwin_x64:
       case TargetPlatform.linux_x64:
+      case TargetPlatform.windows_x64:
         assert(mode == null, 'Platform $platform does not support different build modes.');
         return _getHostArtifactPath(artifact, platform);
     }
@@ -139,6 +146,16 @@
     switch (artifact) {
       case Artifact.skyShell:
       case Artifact.skySnapshot:
+        if (platform == TargetPlatform.windows_x64)
+          throw new UnimplementedError('Artifact $artifact not available on platfrom $platform.');
+        continue returnResourcePath;
+      case Artifact.genSnapshot:
+      case Artifact.vmSnapshotData:
+      case Artifact.isolateSnapshotData:
+        if (platform != TargetPlatform.windows_x64)
+          throw new UnimplementedError('Artifact $artifact not available on platfrom $platform.');
+        continue returnResourcePath;
+      returnResourcePath:
       case Artifact.icudtlDat:
         String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
         String platformDirName = getNameForTargetPlatform(platform);
@@ -147,6 +164,8 @@
         assert(false, 'Artifact $artifact not available for platform $platform.');
         return null;
     }
+    assert(false, 'Artifact $artifact not available for platform $platform.');
+    return null;
   }
 
   String _getEngineArtifactsPath(TargetPlatform platform, [BuildMode mode]) {
@@ -155,6 +174,7 @@
     switch (platform) {
       case TargetPlatform.linux_x64:
       case TargetPlatform.darwin_x64:
+      case TargetPlatform.windows_x64:
         assert(mode == null, 'Platform $platform does not support different build modes.');
         return fs.path.join(engineDir, platformName);
       case TargetPlatform.ios:
@@ -174,6 +194,8 @@
       return TargetPlatform.darwin_x64;
     if (platform.isLinux)
       return TargetPlatform.linux_x64;
+    if (platform.isWindows)
+      return TargetPlatform.windows_x64;
     throw new UnimplementedError('Host OS not supported.');
   }
 }
@@ -193,15 +215,12 @@
       case Artifact.dartIoEntriesTxt:
         return fs.path.join(_engineSrcPath, 'dart', 'runtime', 'bin', _artifactToFileName(artifact));
       case Artifact.dartVmEntryPointsTxt:
-        return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
       case Artifact.dartVmEntryPointsAndroidTxt:
         return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
       case Artifact.snapshotDart:
         return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
       case Artifact.classesDexJar:
         return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', _artifactToFileName(artifact));
-      case Artifact.icudtlDat:
-        return fs.path.join(engineOutPath, _artifactToFileName(artifact));
       case Artifact.libskyShellSo:
         String abi = _getAbiDirectory(platform);
         return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', fs.path.join('android', 'libs', abi, _artifactToFileName(artifact)));
@@ -211,6 +230,10 @@
         return _skyShellPath(platform);
       case Artifact.skySnapshot:
         return _skySnapshotPath();
+      case Artifact.isolateSnapshotData:
+      case Artifact.vmSnapshotData:
+        return fs.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
+      case Artifact.icudtlDat:
       case Artifact.flutterFramework:
         return fs.path.join(engineOutPath, _artifactToFileName(artifact));
     }
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 2b3e697..5facec6 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -66,7 +66,8 @@
   android_x86,
   ios,
   darwin_x64,
-  linux_x64
+  linux_x64,
+  windows_x64
 }
 
 String getNameForTargetPlatform(TargetPlatform platform) {
@@ -83,6 +84,8 @@
       return 'darwin-x64';
     case TargetPlatform.linux_x64:
       return 'linux-x64';
+    case TargetPlatform.windows_x64:
+      return 'windows-x64';
   }
   assert(false);
   return null;
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index e4c751b..bbcafa7 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -289,6 +289,7 @@
   ];
 
   List<List<String>> get _windowsBinaryDirs => <List<String>>[
+    <String>['windows-x64', 'windows-x64/artifacts.zip'],
     <String>['android-arm-profile/windows-x64', 'android-arm-profile/windows-x64.zip'],
     <String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'],
   ];
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index 27529c2..4a7337b 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -173,6 +173,7 @@
       break;
     case TargetPlatform.darwin_x64:
     case TargetPlatform.linux_x64:
+    case TargetPlatform.windows_x64:
       assert(false);
   }
 
@@ -228,6 +229,7 @@
       break;
     case TargetPlatform.darwin_x64:
     case TargetPlatform.linux_x64:
+    case TargetPlatform.windows_x64:
       assert(false);
   }
 
diff --git a/packages/flutter_tools/lib/src/dart/dependencies.dart b/packages/flutter_tools/lib/src/dart/dependencies.dart
index 3c0f10d..d390577 100644
--- a/packages/flutter_tools/lib/src/dart/dependencies.dart
+++ b/packages/flutter_tools/lib/src/dart/dependencies.dart
@@ -5,12 +5,20 @@
 import 'dart:convert';
 
 import '../artifacts.dart';
+import '../base/file_system.dart';
+import '../base/platform.dart';
 import '../base/process.dart';
 
 class DartDependencySetBuilder {
-  DartDependencySetBuilder(this.mainScriptPath,
-                           this.projectRootPath,
-                           this.packagesFilePath);
+
+  factory DartDependencySetBuilder(
+      String mainScriptPath, String projectRootPath, String packagesFilePath) {
+    if (platform.isWindows)
+      return new _GenSnapshotDartDependencySetBuilder(mainScriptPath, projectRootPath, packagesFilePath);
+    return new DartDependencySetBuilder._(mainScriptPath, projectRootPath, packagesFilePath);
+  }
+
+  DartDependencySetBuilder._(this.mainScriptPath, this.projectRootPath, this.packagesFilePath);
 
   final String mainScriptPath;
   final String projectRootPath;
@@ -32,3 +40,58 @@
     return new Set<String>.from(LineSplitter.split(output));
   }
 }
+
+/// A [DartDependencySetBuilder] that is backed by gen_snapshot.
+class _GenSnapshotDartDependencySetBuilder implements DartDependencySetBuilder {
+  _GenSnapshotDartDependencySetBuilder(this.mainScriptPath,
+                                       this.projectRootPath,
+                                       this.packagesFilePath);
+
+  @override
+  final String mainScriptPath;
+  @override
+  final String projectRootPath;
+  @override
+  final String packagesFilePath;
+
+  @override
+  Set<String> build() {
+    final String snapshotterPath =
+        Artifacts.instance.getArtifactPath(Artifact.genSnapshot);
+    final String vmSnapshotData =
+        Artifacts.instance.getArtifactPath(Artifact.vmSnapshotData);
+    final String isolateSnapshotData =
+        Artifacts.instance.getArtifactPath(Artifact.isolateSnapshotData);
+    assert(fs.path.isAbsolute(this.projectRootPath));
+
+    // TODO(goderbauer): Implement --print-deps in gen_snapshot so we don't have to parse the Makefile
+    Directory tempDir = fs.systemTempDirectory.createTempSync('dart_dependency_set_builder_');
+    String depfilePath = fs.path.join(tempDir.path, 'snapshot_blob.bin.d');
+
+    final List<String> args = <String>[
+      snapshotterPath,
+      '--snapshot_kind=script',
+      '--dependencies-only',
+      '--vm_snapshot_data=$vmSnapshotData',
+      '--isolate_snapshot_data=$isolateSnapshotData',
+      '--packages=$packagesFilePath',
+      '--dependencies=$depfilePath',
+      '--script_snapshot=snapshot_blob.bin',
+      mainScriptPath
+    ];
+
+    runSyncAndThrowStdErrOnError(args);
+    String output = fs.file(depfilePath).readAsStringSync();
+    tempDir.deleteSync(recursive: true);
+
+    int splitIndex = output.indexOf(':');
+    if (splitIndex == -1)
+      throw new Exception('Unexpected output $output');
+
+    output = output.substring(splitIndex + 1);
+    // Note: next line means we cannot process anything with spaces in the path
+    //       because Makefiles don't support spaces in paths :(
+    List<String> depsList = output.trim().split(' ');
+    return new Set<String>.from(depsList);
+  }
+}
diff --git a/packages/flutter_tools/lib/src/flx.dart b/packages/flutter_tools/lib/src/flx.dart
index b35ccbf..bd0078f 100644
--- a/packages/flutter_tools/lib/src/flx.dart
+++ b/packages/flutter_tools/lib/src/flx.dart
@@ -8,6 +8,7 @@
 import 'asset.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
+import 'base/platform.dart';
 import 'base/process.dart';
 import 'build_info.dart';
 import 'dart/package_map.dart';
@@ -28,16 +29,37 @@
 const String _kSnapshotKey = 'snapshot_blob.bin';
 
 Future<int> createSnapshot({
-  String snapshotterPath,
   String mainPath,
   String snapshotPath,
   String depfilePath,
   String packages
 }) {
-  assert(snapshotterPath != null);
+  if (platform.isWindows) {
+    return _creteScriptSnapshotWithGenSnapshot(
+        mainPath: mainPath,
+        snapshotPath: snapshotPath,
+        depfilePath: depfilePath,
+        packages: packages
+    );
+  }
+  return _createScriptSnapshotWithSkySnapshot(
+      mainPath: mainPath,
+      snapshotPath: snapshotPath,
+      depfilePath: depfilePath,
+      packages: packages
+  );
+}
+
+Future<int> _createScriptSnapshotWithSkySnapshot({
+  String mainPath,
+  String snapshotPath,
+  String depfilePath,
+  String packages
+}) {
   assert(mainPath != null);
   assert(snapshotPath != null);
   assert(packages != null);
+  String snapshotterPath = artifacts.getArtifactPath(Artifact.skySnapshot);
 
   final List<String> args = <String>[
     snapshotterPath,
@@ -52,6 +74,34 @@
   return runCommandAndStreamOutput(args);
 }
 
+Future<int> _creteScriptSnapshotWithGenSnapshot({
+  String mainPath,
+  String snapshotPath,
+  String depfilePath,
+  String packages
+}) {
+  assert(mainPath != null);
+  assert(snapshotPath != null);
+  assert(packages != null);
+  String snapshotterPath = artifacts.getArtifactPath(Artifact.genSnapshot);
+  String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData);
+  String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData);
+
+  final List<String> args = <String>[
+    snapshotterPath,
+    '--snapshot_kind=script',
+    '--vm_snapshot_data=$vmSnapshotData',
+    '--isolate_snapshot_data=$isolateSnapshotData',
+    '--packages=$packages',
+    '--script_snapshot=$snapshotPath'
+  ];
+  if (depfilePath != null) {
+    args.add('--dependencies=$depfilePath');
+  }
+  args.add(mainPath);
+  return runCommandAndStreamOutput(args);
+}
+
 /// Build the flx in the build directory and return `localBundlePath` on success.
 ///
 /// Return `null` on failure.
@@ -73,7 +123,6 @@
 }
 
 Future<Null> build({
-  String snapshotterPath,
   String mainPath: defaultMainPath,
   String manifestPath: defaultManifestPath,
   String outputPath,
@@ -110,7 +159,6 @@
     // In a precompiled snapshot, the instruction buffer contains script
     // content equivalents
     int result = await createSnapshot(
-      snapshotterPath: snapshotterPath ?? artifacts.getArtifactPath(Artifact.skySnapshot),
       mainPath: mainPath,
       snapshotPath: snapshotPath,
       depfilePath: depfilePath,
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index b09fdf4..87baf60 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -161,8 +161,10 @@
       ProcessSignal.SIGTERM.watch().listen(_cleanUpAndExit);
     if (!supportsServiceProtocol || !supportsRestart)
       return;
-    ProcessSignal.SIGUSR1.watch().listen(_handleSignal);
-    ProcessSignal.SIGUSR2.watch().listen(_handleSignal);
+    if (!platform.isWindows) {
+      ProcessSignal.SIGUSR1.watch().listen(_handleSignal);
+      ProcessSignal.SIGUSR2.watch().listen(_handleSignal);
+    }
   }
 
   Future<Null> _cleanUpAndExit(ProcessSignal signal) async {
diff --git a/packages/flutter_tools/test/dart_dependencies_test.dart b/packages/flutter_tools/test/dart_dependencies_test.dart
index 0bb6d1f..9784d67 100644
--- a/packages/flutter_tools/test/dart_dependencies_test.dart
+++ b/packages/flutter_tools/test/dart_dependencies_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:io' as io;
-
 import 'package:flutter_tools/src/dart/dependencies.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/platform.dart';
@@ -12,7 +10,7 @@
 
 void main()  {
   group('DartDependencySetBuilder', () {
-    final String basePath = fs.path.dirname(platform.script.path);
+    final String basePath = fs.path.dirname(fs.path.fromUri(platform.script));
     final String dataPath = fs.path.join(basePath, 'data', 'dart_dependencies_test');
     testUsingContext('good', () {
       final String testPath = fs.path.join(dataPath, 'good');
@@ -38,5 +36,5 @@
         expect(e.contains('unexpected token \'bad\''), isTrue);
       }
     });
-  }, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_snapshot is available
+  });
 }
diff --git a/packages/flutter_tools/test/dependency_checker_test.dart b/packages/flutter_tools/test/dependency_checker_test.dart
index 8fb4ee4..30ea979 100644
--- a/packages/flutter_tools/test/dependency_checker_test.dart
+++ b/packages/flutter_tools/test/dependency_checker_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:io' as io;
-
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/platform.dart';
@@ -56,7 +54,7 @@
       // Set 'package:self/bar.dart' file modification time to be in the future.
       updateFileModificationTime(barPath, baseTime, 10);
       expect(dependencyChecker.check(baseTime), isTrue);
-    }, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_snapshot is ready on Windows
+    });
 
     testUsingContext('syntax error', () {
       final String testPath = fs.path.join(dataPath, 'syntax_error');