Commit 644e7b13 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Faster hot reload (#8600)

This implements the `DartDependencySetBuilder` completely in Dart instead of calling out to `sky_snapshot` (Linux/Mac) or `gen_snapshot` (Windows) and allows us to use the same code path on all supported host platforms.

It also slightly reduces hot reload times on Linux from ~750ms to ~690ms for the unchanged flutter_gallery app and significantly reduces hot reload times on Windows from almost 1.5s to just slightly slower than on Linux.

This change will also allow us to retire `sky_snapshot` completely in the future.
parent 3b4f6b1a
......@@ -2,89 +2,49 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'package:analyzer/analyzer.dart';
import '../artifacts.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../dart/package_map.dart';
class DartDependencySetBuilder {
DartDependencySetBuilder(String mainScriptPath, String packagesFilePath) :
this._mainScriptPath = fs.path.canonicalize(mainScriptPath),
this._mainScriptUri = fs.path.toUri(mainScriptPath),
this._packagesFilePath = fs.path.canonicalize(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 _packagesFilePath;
final String mainScriptPath;
final String projectRootPath;
final String packagesFilePath;
final Uri _mainScriptUri;
/// Returns a set of canonicalize paths.
///
/// The paths have been canonicalize with `fs.path.canonicalize`.
Set<String> build() {
final String skySnapshotPath =
Artifacts.instance.getArtifactPath(Artifact.skySnapshot);
final List<String> args = <String>[
skySnapshotPath,
'--packages=$packagesFilePath',
'--print-deps',
mainScriptPath
];
final String output = runSyncAndThrowStdErrOnError(args);
return new Set<String>.from(LineSplitter.split(output).map(
(String path) => fs.path.canonicalize(path))
);
final List<String> dependencies = <String>[_mainScriptPath, _packagesFilePath];
final List<Uri> toProcess = <Uri>[_mainScriptUri];
final PackageMap packageMap = new PackageMap(_packagesFilePath);
while (toProcess.isNotEmpty) {
final Uri currentUri = toProcess.removeLast();
final CompilationUnit unit = _parse(currentUri.toFilePath());
for (Directive directive in unit.directives) {
if (!(directive is UriBasedDirective))
continue;
final UriBasedDirective uriBasedDirective = directive;
final String uriAsString = uriBasedDirective.uri.stringValue;
Uri resolvedUri = resolveRelativeUri(currentUri, Uri.parse(uriAsString));
if (resolvedUri.scheme.startsWith('dart'))
continue;
if (resolvedUri.scheme == 'package')
resolvedUri = packageMap.uriForPackage(resolvedUri);
final String path = fs.path.canonicalize(resolvedUri.toFilePath());
if (!dependencies.contains(path)) {
dependencies.add(path);
toProcess.add(resolvedUri);
}
}
}
return dependencies.toSet();
}
}
/// 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));
final List<String> args = <String>[
snapshotterPath,
'--snapshot_kind=script',
'--dependencies-only',
'--vm_snapshot_data=$vmSnapshotData',
'--isolate_snapshot_data=$isolateSnapshotData',
'--packages=$packagesFilePath',
'--print-dependencies',
'--script_snapshot=snapshot_blob.bin',
mainScriptPath
];
final String output = runSyncAndThrowStdErrOnError(args);
return new Set<String>.from(LineSplitter.split(output).map(
(String path) => fs.path.canonicalize(path))
);
}
CompilationUnit _parse(String path) => parseDirectives(fs.file(path).readAsStringSync(), name: path);
}
......@@ -40,13 +40,16 @@ class PackageMap {
Map<String, Uri> _map;
/// Returns the path to [packageUri].
String pathForPackage(Uri packageUri) {
String pathForPackage(Uri packageUri) => uriForPackage(packageUri).path;
/// Returns the path to [packageUri] as Uri.
Uri uriForPackage(Uri packageUri) {
assert(packageUri.scheme == 'package');
final List<String> pathSegments = packageUri.pathSegments.toList();
final String packageName = pathSegments.removeAt(0);
final Uri packageBase = map[packageName];
final String packageRelativePath = fs.path.joinAll(pathSegments);
return packageBase.resolve(packageRelativePath).path;
return packageBase.resolveUri(fs.path.toUri(packageRelativePath));
}
String checkValid() {
......
......@@ -17,12 +17,6 @@ class DependencyChecker {
/// if it cannot be determined.
bool check(DateTime threshold) {
_dependencies.clear();
try {
_dependencies.add(builder.packagesFilePath);
} catch (e, st) {
printTrace('DependencyChecker: could not parse .packages file:\n$e\n$st');
return true;
}
// Build the set of Dart dependencies.
try {
_dependencies.addAll(builder.build());
......
......@@ -330,8 +330,7 @@ abstract class ResidentRunner {
bool hasDirtyDependencies() {
final DartDependencySetBuilder dartDependencySetBuilder =
new DartDependencySetBuilder(
mainPath, projectRootPath, packagesFilePath);
new DartDependencySetBuilder(mainPath, packagesFilePath);
final DependencyChecker dependencyChecker =
new DependencyChecker(dartDependencySetBuilder, assetBundle);
final String path = package.packagePath;
......
......@@ -99,8 +99,7 @@ class HotRunner extends ResidentRunner {
return true;
}
final DartDependencySetBuilder dartDependencySetBuilder =
new DartDependencySetBuilder(
mainPath, projectRootPath, packagesFilePath);
new DartDependencySetBuilder(mainPath, packagesFilePath);
try {
_dartDependencies = new Set<String>.from(dartDependencySetBuilder.build());
} catch (error) {
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:analyzer/analyzer.dart';
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';
......@@ -17,7 +18,7 @@ void main() {
final String mainPath = fs.path.join(testPath, 'main.dart');
final String packagesPath = fs.path.join(testPath, '.packages');
final DartDependencySetBuilder builder =
new DartDependencySetBuilder(mainPath, testPath, packagesPath);
new DartDependencySetBuilder(mainPath, packagesPath);
final Set<String> dependencies = builder.build();
expect(dependencies.contains(fs.path.canonicalize(mainPath)), isTrue);
expect(dependencies.contains(fs.path.canonicalize(fs.path.join(testPath, 'foo.dart'))), isTrue);
......@@ -27,13 +28,12 @@ void main() {
final String mainPath = fs.path.join(testPath, 'main.dart');
final String packagesPath = fs.path.join(testPath, '.packages');
final DartDependencySetBuilder builder =
new DartDependencySetBuilder(mainPath, testPath, packagesPath);
new DartDependencySetBuilder(mainPath, packagesPath);
try {
builder.build();
fail('expect an assertion to be thrown.');
} catch (e) {
expect(e, const isInstanceOf<String>());
expect(e.contains('unexpected token \'bad\''), isTrue);
} on AnalyzerErrorGroup catch (e) {
expect(e.toString(), contains('foo.dart: Expected a string literal'));
}
});
});
......
......@@ -2,4 +2,4 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
bad programmer!
import bad programmer!
......@@ -31,7 +31,7 @@ void main() {
final String barPath = fs.path.join(testPath, 'lib', 'bar.dart');
final String packagesPath = fs.path.join(testPath, '.packages');
final DartDependencySetBuilder builder =
new DartDependencySetBuilder(mainPath, testPath, packagesPath);
new DartDependencySetBuilder(mainPath, packagesPath);
final DependencyChecker dependencyChecker =
new DependencyChecker(builder, null);
......@@ -63,7 +63,7 @@ void main() {
final String packagesPath = fs.path.join(testPath, '.packages');
final DartDependencySetBuilder builder =
new DartDependencySetBuilder(mainPath, testPath, packagesPath);
new DartDependencySetBuilder(mainPath, packagesPath);
final DependencyChecker dependencyChecker =
new DependencyChecker(builder, null);
......
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