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
\ No newline at end of file
342a31136e2c333bfc8a69edb12efd6231fbd5ea
......@@ -258,6 +258,7 @@ ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, {
: 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 @@ class ApplicationPackageStore {
return iOS;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
return null;
}
return null;
......
......@@ -21,6 +21,8 @@ enum Artifact {
skySnapshot,
snapshotDart,
flutterFramework,
vmSnapshotData,
isolateSnapshotData
}
String _artifactToFileName(Artifact artifact) {
......@@ -49,6 +51,10 @@ String _artifactToFileName(Artifact artifact) {
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 @@ class CachedArtifacts extends Artifacts {
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 @@ class CachedArtifacts extends Artifacts {
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 @@ class CachedArtifacts extends Artifacts {
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 @@ class CachedArtifacts extends Artifacts {
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 @@ class CachedArtifacts extends Artifacts {
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 @@ class LocalEngineArtifacts extends Artifacts {
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 @@ class LocalEngineArtifacts extends Artifacts {
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));
}
......
......@@ -66,7 +66,8 @@ enum TargetPlatform {
android_x86,
ios,
darwin_x64,
linux_x64
linux_x64,
windows_x64
}
String getNameForTargetPlatform(TargetPlatform platform) {
......@@ -83,6 +84,8 @@ String getNameForTargetPlatform(TargetPlatform platform) {
return 'darwin-x64';
case TargetPlatform.linux_x64:
return 'linux-x64';
case TargetPlatform.windows_x64:
return 'windows-x64';
}
assert(false);
return null;
......
......@@ -289,6 +289,7 @@ class FlutterEngine {
];
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'],
];
......
......@@ -173,6 +173,7 @@ Future<String> _buildAotSnapshot(
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
assert(false);
}
......@@ -228,6 +229,7 @@ Future<String> _buildAotSnapshot(
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
assert(false);
}
......
......@@ -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 @@ class DartDependencySetBuilder {
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';
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 _kKernelKey = 'kernel_blob.bin';
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 @@ Future<int> createSnapshot({
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<String> buildFlx({
}
Future<Null> build({
String snapshotterPath,
String mainPath: defaultMainPath,
String manifestPath: defaultManifestPath,
String outputPath,
......@@ -110,7 +159,6 @@ Future<Null> build({
// 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,
......
......@@ -161,9 +161,11 @@ abstract class ResidentRunner {
ProcessSignal.SIGTERM.watch().listen(_cleanUpAndExit);
if (!supportsServiceProtocol || !supportsRestart)
return;
if (!platform.isWindows) {
ProcessSignal.SIGUSR1.watch().listen(_handleSignal);
ProcessSignal.SIGUSR2.watch().listen(_handleSignal);
}
}
Future<Null> _cleanUpAndExit(ProcessSignal signal) async {
_resetTerminal();
......
......@@ -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 @@ import 'src/context.dart';
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 @@ void main() {
expect(e.contains('unexpected token \'bad\''), isTrue);
}
});
}, skip: io.Platform.isWindows); // TODO(goderbauer): enable when sky_snapshot is available
});
}
......@@ -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 @@ void main() {
// 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');
......
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