Commit d579d587 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

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
parent 55e10a66
74de13c0bde4eeb967391bd2a7ba973c525113b1 342a31136e2c333bfc8a69edb12efd6231fbd5ea
\ No newline at end of file
...@@ -258,6 +258,7 @@ ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, { ...@@ -258,6 +258,7 @@ ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, {
: new IOSApp.fromIpa(applicationBinary); : new IOSApp.fromIpa(applicationBinary);
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
return null; return null;
} }
assert(platform != null); assert(platform != null);
...@@ -282,6 +283,7 @@ class ApplicationPackageStore { ...@@ -282,6 +283,7 @@ class ApplicationPackageStore {
return iOS; return iOS;
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
return null; return null;
} }
return null; return null;
......
...@@ -21,6 +21,8 @@ enum Artifact { ...@@ -21,6 +21,8 @@ enum Artifact {
skySnapshot, skySnapshot,
snapshotDart, snapshotDart,
flutterFramework, flutterFramework,
vmSnapshotData,
isolateSnapshotData
} }
String _artifactToFileName(Artifact artifact) { String _artifactToFileName(Artifact artifact) {
...@@ -49,6 +51,10 @@ String _artifactToFileName(Artifact artifact) { ...@@ -49,6 +51,10 @@ String _artifactToFileName(Artifact artifact) {
return 'snapshot.dart'; return 'snapshot.dart';
case Artifact.flutterFramework: case Artifact.flutterFramework:
return 'Flutter.framework'; return 'Flutter.framework';
case Artifact.vmSnapshotData:
return 'vm_isolate_snapshot.bin';
case Artifact.isolateSnapshotData:
return 'isolate_snapshot.bin';
} }
assert(false, 'Invalid artifact $artifact.'); assert(false, 'Invalid artifact $artifact.');
return null; return null;
...@@ -85,6 +91,7 @@ class CachedArtifacts extends Artifacts { ...@@ -85,6 +91,7 @@ class CachedArtifacts extends Artifacts {
return _getIosArtifactPath(artifact, platform, mode); return _getIosArtifactPath(artifact, platform, mode);
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
assert(mode == null, 'Platform $platform does not support different build modes.'); assert(mode == null, 'Platform $platform does not support different build modes.');
return _getHostArtifactPath(artifact, platform); return _getHostArtifactPath(artifact, platform);
} }
...@@ -139,6 +146,16 @@ class CachedArtifacts extends Artifacts { ...@@ -139,6 +146,16 @@ class CachedArtifacts extends Artifacts {
switch (artifact) { switch (artifact) {
case Artifact.skyShell: case Artifact.skyShell:
case Artifact.skySnapshot: 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: case Artifact.icudtlDat:
String engineArtifactsPath = cache.getArtifactDirectory('engine').path; String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
String platformDirName = getNameForTargetPlatform(platform); String platformDirName = getNameForTargetPlatform(platform);
...@@ -147,6 +164,8 @@ class CachedArtifacts extends Artifacts { ...@@ -147,6 +164,8 @@ class CachedArtifacts extends Artifacts {
assert(false, 'Artifact $artifact not available for platform $platform.'); assert(false, 'Artifact $artifact not available for platform $platform.');
return null; return null;
} }
assert(false, 'Artifact $artifact not available for platform $platform.');
return null;
} }
String _getEngineArtifactsPath(TargetPlatform platform, [BuildMode mode]) { String _getEngineArtifactsPath(TargetPlatform platform, [BuildMode mode]) {
...@@ -155,6 +174,7 @@ class CachedArtifacts extends Artifacts { ...@@ -155,6 +174,7 @@ class CachedArtifacts extends Artifacts {
switch (platform) { switch (platform) {
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.windows_x64:
assert(mode == null, 'Platform $platform does not support different build modes.'); assert(mode == null, 'Platform $platform does not support different build modes.');
return fs.path.join(engineDir, platformName); return fs.path.join(engineDir, platformName);
case TargetPlatform.ios: case TargetPlatform.ios:
...@@ -174,6 +194,8 @@ class CachedArtifacts extends Artifacts { ...@@ -174,6 +194,8 @@ class CachedArtifacts extends Artifacts {
return TargetPlatform.darwin_x64; return TargetPlatform.darwin_x64;
if (platform.isLinux) if (platform.isLinux)
return TargetPlatform.linux_x64; return TargetPlatform.linux_x64;
if (platform.isWindows)
return TargetPlatform.windows_x64;
throw new UnimplementedError('Host OS not supported.'); throw new UnimplementedError('Host OS not supported.');
} }
} }
...@@ -193,15 +215,12 @@ class LocalEngineArtifacts extends Artifacts { ...@@ -193,15 +215,12 @@ class LocalEngineArtifacts extends Artifacts {
case Artifact.dartIoEntriesTxt: case Artifact.dartIoEntriesTxt:
return fs.path.join(_engineSrcPath, 'dart', 'runtime', 'bin', _artifactToFileName(artifact)); return fs.path.join(_engineSrcPath, 'dart', 'runtime', 'bin', _artifactToFileName(artifact));
case Artifact.dartVmEntryPointsTxt: case Artifact.dartVmEntryPointsTxt:
return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
case Artifact.dartVmEntryPointsAndroidTxt: case Artifact.dartVmEntryPointsAndroidTxt:
return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact)); return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
case Artifact.snapshotDart: case Artifact.snapshotDart:
return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact)); return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
case Artifact.classesDexJar: case Artifact.classesDexJar:
return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', _artifactToFileName(artifact)); 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: case Artifact.libskyShellSo:
String abi = _getAbiDirectory(platform); String abi = _getAbiDirectory(platform);
return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', fs.path.join('android', 'libs', abi, _artifactToFileName(artifact))); return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', fs.path.join('android', 'libs', abi, _artifactToFileName(artifact)));
...@@ -211,6 +230,10 @@ class LocalEngineArtifacts extends Artifacts { ...@@ -211,6 +230,10 @@ class LocalEngineArtifacts extends Artifacts {
return _skyShellPath(platform); return _skyShellPath(platform);
case Artifact.skySnapshot: case Artifact.skySnapshot:
return _skySnapshotPath(); 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: case Artifact.flutterFramework:
return fs.path.join(engineOutPath, _artifactToFileName(artifact)); return fs.path.join(engineOutPath, _artifactToFileName(artifact));
} }
......
...@@ -66,7 +66,8 @@ enum TargetPlatform { ...@@ -66,7 +66,8 @@ enum TargetPlatform {
android_x86, android_x86,
ios, ios,
darwin_x64, darwin_x64,
linux_x64 linux_x64,
windows_x64
} }
String getNameForTargetPlatform(TargetPlatform platform) { String getNameForTargetPlatform(TargetPlatform platform) {
...@@ -83,6 +84,8 @@ String getNameForTargetPlatform(TargetPlatform platform) { ...@@ -83,6 +84,8 @@ String getNameForTargetPlatform(TargetPlatform platform) {
return 'darwin-x64'; return 'darwin-x64';
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
return 'linux-x64'; return 'linux-x64';
case TargetPlatform.windows_x64:
return 'windows-x64';
} }
assert(false); assert(false);
return null; return null;
......
...@@ -289,6 +289,7 @@ class FlutterEngine { ...@@ -289,6 +289,7 @@ class FlutterEngine {
]; ];
List<List<String>> get _windowsBinaryDirs => <List<String>>[ 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-profile/windows-x64', 'android-arm-profile/windows-x64.zip'],
<String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'], <String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'],
]; ];
......
...@@ -173,6 +173,7 @@ Future<String> _buildAotSnapshot( ...@@ -173,6 +173,7 @@ Future<String> _buildAotSnapshot(
break; break;
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
assert(false); assert(false);
} }
...@@ -228,6 +229,7 @@ Future<String> _buildAotSnapshot( ...@@ -228,6 +229,7 @@ Future<String> _buildAotSnapshot(
break; break;
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
assert(false); assert(false);
} }
......
...@@ -5,12 +5,20 @@ ...@@ -5,12 +5,20 @@
import 'dart:convert'; import 'dart:convert';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
class DartDependencySetBuilder { class DartDependencySetBuilder {
DartDependencySetBuilder(this.mainScriptPath,
this.projectRootPath, factory DartDependencySetBuilder(
this.packagesFilePath); 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 mainScriptPath;
final String projectRootPath; final String projectRootPath;
...@@ -32,3 +40,58 @@ class DartDependencySetBuilder { ...@@ -32,3 +40,58 @@ class DartDependencySetBuilder {
return new Set<String>.from(LineSplitter.split(output)); 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);
}
}
...@@ -8,6 +8,7 @@ import 'artifacts.dart'; ...@@ -8,6 +8,7 @@ import 'artifacts.dart';
import 'asset.dart'; import 'asset.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/platform.dart';
import 'base/process.dart'; import 'base/process.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'dart/package_map.dart'; import 'dart/package_map.dart';
...@@ -28,16 +29,37 @@ const String _kKernelKey = 'kernel_blob.bin'; ...@@ -28,16 +29,37 @@ const String _kKernelKey = 'kernel_blob.bin';
const String _kSnapshotKey = 'snapshot_blob.bin'; const String _kSnapshotKey = 'snapshot_blob.bin';
Future<int> createSnapshot({ Future<int> createSnapshot({
String snapshotterPath,
String mainPath, String mainPath,
String snapshotPath, String snapshotPath,
String depfilePath, String depfilePath,
String packages 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(mainPath != null);
assert(snapshotPath != null); assert(snapshotPath != null);
assert(packages != null); assert(packages != null);
String snapshotterPath = artifacts.getArtifactPath(Artifact.skySnapshot);
final List<String> args = <String>[ final List<String> args = <String>[
snapshotterPath, snapshotterPath,
...@@ -52,6 +74,34 @@ Future<int> createSnapshot({ ...@@ -52,6 +74,34 @@ Future<int> createSnapshot({
return runCommandAndStreamOutput(args); 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. /// Build the flx in the build directory and return `localBundlePath` on success.
/// ///
/// Return `null` on failure. /// Return `null` on failure.
...@@ -73,7 +123,6 @@ Future<String> buildFlx({ ...@@ -73,7 +123,6 @@ Future<String> buildFlx({
} }
Future<Null> build({ Future<Null> build({
String snapshotterPath,
String mainPath: defaultMainPath, String mainPath: defaultMainPath,
String manifestPath: defaultManifestPath, String manifestPath: defaultManifestPath,
String outputPath, String outputPath,
...@@ -110,7 +159,6 @@ Future<Null> build({ ...@@ -110,7 +159,6 @@ Future<Null> build({
// In a precompiled snapshot, the instruction buffer contains script // In a precompiled snapshot, the instruction buffer contains script
// content equivalents // content equivalents
int result = await createSnapshot( int result = await createSnapshot(
snapshotterPath: snapshotterPath ?? artifacts.getArtifactPath(Artifact.skySnapshot),
mainPath: mainPath, mainPath: mainPath,
snapshotPath: snapshotPath, snapshotPath: snapshotPath,
depfilePath: depfilePath, depfilePath: depfilePath,
......
...@@ -161,8 +161,10 @@ abstract class ResidentRunner { ...@@ -161,8 +161,10 @@ abstract class ResidentRunner {
ProcessSignal.SIGTERM.watch().listen(_cleanUpAndExit); ProcessSignal.SIGTERM.watch().listen(_cleanUpAndExit);
if (!supportsServiceProtocol || !supportsRestart) if (!supportsServiceProtocol || !supportsRestart)
return; return;
ProcessSignal.SIGUSR1.watch().listen(_handleSignal); if (!platform.isWindows) {
ProcessSignal.SIGUSR2.watch().listen(_handleSignal); ProcessSignal.SIGUSR1.watch().listen(_handleSignal);
ProcessSignal.SIGUSR2.watch().listen(_handleSignal);
}
} }
Future<Null> _cleanUpAndExit(ProcessSignal signal) async { Future<Null> _cleanUpAndExit(ProcessSignal signal) async {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:io' as io;
import 'package:flutter_tools/src/dart/dependencies.dart'; import 'package:flutter_tools/src/dart/dependencies.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -12,7 +10,7 @@ import 'src/context.dart'; ...@@ -12,7 +10,7 @@ import 'src/context.dart';
void main() { void main() {
group('DartDependencySetBuilder', () { 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'); final String dataPath = fs.path.join(basePath, 'data', 'dart_dependencies_test');
testUsingContext('good', () { testUsingContext('good', () {
final String testPath = fs.path.join(dataPath, 'good'); final String testPath = fs.path.join(dataPath, 'good');
...@@ -38,5 +36,5 @@ void main() { ...@@ -38,5 +36,5 @@ void main() {
expect(e.contains('unexpected token \'bad\''), isTrue); expect(e.contains('unexpected token \'bad\''), isTrue);
} }
}); });
}, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_snapshot is available });
} }
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:io' as io;
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -56,7 +54,7 @@ void main() { ...@@ -56,7 +54,7 @@ void main() {
// Set 'package:self/bar.dart' file modification time to be in the future. // Set 'package:self/bar.dart' file modification time to be in the future.
updateFileModificationTime(barPath, baseTime, 10); updateFileModificationTime(barPath, baseTime, 10);
expect(dependencyChecker.check(baseTime), isTrue); expect(dependencyChecker.check(baseTime), isTrue);
}, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_snapshot is ready on Windows });
testUsingContext('syntax error', () { testUsingContext('syntax error', () {
final String testPath = fs.path.join(dataPath, 'syntax_error'); final String testPath = fs.path.join(dataPath, 'syntax_error');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment