Commit 594c4f99 authored by John McCutchan's avatar John McCutchan Committed by GitHub

Only synchronize used Dart sources to DevFS. (#6668)

- [x] Remove the second full-sync on startup.
- [x] Always invoke the snapshotter to determine the minimal set of Dart sources used.
- [x] Only synchronize the *used* Dart sources to DevFS.
- [x] Detect syntax / file missing errors on the host and gate reloads / restarts.
parent 2d9d4c2d
...@@ -164,6 +164,16 @@ String runCheckedSync(List<String> cmd, { ...@@ -164,6 +164,16 @@ String runCheckedSync(List<String> cmd, {
); );
} }
/// Run cmd and return stdout on success.
///
/// Throws the standard error output if cmd exits with a non-zero value.
String runSyncAndThrowStdErrOnError(List<String> cmd) {
return _runWithLoggingSync(cmd,
checked: true,
throwStandardErrorOnError: true,
hideStdout: true);
}
/// Run cmd and return stdout. /// Run cmd and return stdout.
String runSync(List<String> cmd, { String runSync(List<String> cmd, {
String workingDirectory, String workingDirectory,
...@@ -187,6 +197,7 @@ void _traceCommand(List<String> args, { String workingDirectory }) { ...@@ -187,6 +197,7 @@ void _traceCommand(List<String> args, { String workingDirectory }) {
String _runWithLoggingSync(List<String> cmd, { String _runWithLoggingSync(List<String> cmd, {
bool checked: false, bool checked: false,
bool noisyErrors: false, bool noisyErrors: false,
bool throwStandardErrorOnError: false,
String workingDirectory, String workingDirectory,
bool allowReentrantFlutter: false, bool allowReentrantFlutter: false,
bool hideStdout: false, bool hideStdout: false,
...@@ -216,6 +227,9 @@ String _runWithLoggingSync(List<String> cmd, { ...@@ -216,6 +227,9 @@ String _runWithLoggingSync(List<String> cmd, {
printTrace(results.stderr.trim()); printTrace(results.stderr.trim());
} }
if (throwStandardErrorOnError)
throw results.stderr.trim();
if (checked) if (checked)
throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}'; throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}';
} }
......
...@@ -38,8 +38,8 @@ String getDevFSLoaderScript() { ...@@ -38,8 +38,8 @@ String getDevFSLoaderScript() {
'loader_app.dart')); 'loader_app.dart'));
} }
class StartupDependencySetBuilder { class DartDependencySetBuilder {
StartupDependencySetBuilder(this.mainScriptPath, DartDependencySetBuilder(this.mainScriptPath,
this.projectRootPath); this.projectRootPath);
final String mainScriptPath; final String mainScriptPath;
...@@ -56,12 +56,7 @@ class StartupDependencySetBuilder { ...@@ -56,12 +56,7 @@ class StartupDependencySetBuilder {
mainScriptPath mainScriptPath
]; ];
String output; String output = runSyncAndThrowStdErrOnError(args);
try {
output = runCheckedSync(args, hideStdout: true);
} catch (e) {
return null;
}
final List<String> lines = LineSplitter.split(output).toList(); final List<String> lines = LineSplitter.split(output).toList();
final Set<String> minimalDependencies = new Set<String>(); final Set<String> minimalDependencies = new Set<String>();
...@@ -135,7 +130,7 @@ class HotRunner extends ResidentRunner { ...@@ -135,7 +130,7 @@ class HotRunner extends ResidentRunner {
ApplicationPackage _package; ApplicationPackage _package;
String _mainPath; String _mainPath;
String _projectRootPath; String _projectRootPath;
Set<String> _startupDependencies; Set<String> _dartDependencies;
int _observatoryPort; int _observatoryPort;
final AssetBundle bundle = new AssetBundle(); final AssetBundle bundle = new AssetBundle();
final bool benchmarkMode; final bool benchmarkMode;
...@@ -159,6 +154,23 @@ class HotRunner extends ResidentRunner { ...@@ -159,6 +154,23 @@ class HotRunner extends ResidentRunner {
}); });
} }
bool _refreshDartDependencies() {
if (_dartDependencies != null) {
// Already computed.
return true;
}
DartDependencySetBuilder dartDependencySetBuilder =
new DartDependencySetBuilder(_mainPath, _projectRootPath);
try {
_dartDependencies = dartDependencySetBuilder.build();
} catch (error) {
printStatus('Error detected in application source code:', emphasis: true);
printError(error);
return false;
}
return true;
}
Future<int> _run({ Future<int> _run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
String route, String route,
...@@ -184,6 +196,12 @@ class HotRunner extends ResidentRunner { ...@@ -184,6 +196,12 @@ class HotRunner extends ResidentRunner {
return 1; return 1;
} }
// Determine the Dart dependencies eagerly.
if (!_refreshDartDependencies()) {
// Some kind of source level error or missing file in the Dart code.
return 1;
}
// TODO(devoncarew): We shouldn't have to do type checks here. // TODO(devoncarew): We shouldn't have to do type checks here.
if (shouldBuild && device is AndroidDevice) { if (shouldBuild && device is AndroidDevice) {
printTrace('Running build command.'); printTrace('Running build command.');
...@@ -231,15 +249,6 @@ class HotRunner extends ResidentRunner { ...@@ -231,15 +249,6 @@ class HotRunner extends ResidentRunner {
route: route route: route
); );
// In parallel, compute the minimal dependency set.
StartupDependencySetBuilder startupDependencySetBuilder =
new StartupDependencySetBuilder(_mainPath, _projectRootPath);
_startupDependencies = startupDependencySetBuilder.build();
if (_startupDependencies == null) {
printError('Error determining the set of Dart sources necessary to start '
'the application. Initial file upload may take a long time.');
}
LaunchResult result = await futureResult; LaunchResult result = await futureResult;
if (!result.started) { if (!result.started) {
...@@ -295,10 +304,6 @@ class HotRunner extends ResidentRunner { ...@@ -295,10 +304,6 @@ class HotRunner extends ResidentRunner {
registerSignalHandlers(); registerSignalHandlers();
printTrace('Finishing file synchronization');
// Finish the file sync now.
await _updateDevFS();
if (benchmarkMode) { if (benchmarkMode) {
// We are running in benchmark mode. // We are running in benchmark mode.
printStatus('Running in benchmark mode.'); printStatus('Running in benchmark mode.');
...@@ -325,13 +330,19 @@ class HotRunner extends ResidentRunner { ...@@ -325,13 +330,19 @@ class HotRunner extends ResidentRunner {
Future<Null> handleTerminalCommand(String code) async { Future<Null> handleTerminalCommand(String code) async {
final String lower = code.toLowerCase(); final String lower = code.toLowerCase();
if ((lower == 'r') || (code == AnsiTerminal.KEY_F5)) { if ((lower == 'r') || (code == AnsiTerminal.KEY_F5)) {
OperationResult result = OperationResult.ok;
// F5, restart // F5, restart
if ((code == 'r') || (code == AnsiTerminal.KEY_F5)) { if ((code == 'r') || (code == AnsiTerminal.KEY_F5)) {
// lower-case 'r' // lower-case 'r'
await _reloadSources(); result = await _reloadSources();
} else { } else {
// upper-case 'R'. // upper-case 'R'.
await _restartFromSources(); result = await _restartFromSources();
}
if (!result.isOk) {
// TODO(johnmccutchan): Attempt to determine the number of errors that
// occurred and tighten this message.
printStatus('Try again after fixing the above error(s).', emphasis: true);
} }
} }
} }
...@@ -362,6 +373,10 @@ class HotRunner extends ResidentRunner { ...@@ -362,6 +373,10 @@ class HotRunner extends ResidentRunner {
} }
Future<bool> _updateDevFS({ DevFSProgressReporter progressReporter }) async { Future<bool> _updateDevFS({ DevFSProgressReporter progressReporter }) async {
if (!_refreshDartDependencies()) {
// Did not update DevFS because of a Dart source error.
return false;
}
Status devFSStatus = logger.startProgress('Syncing files to device...'); Status devFSStatus = logger.startProgress('Syncing files to device...');
final bool rebuildBundle = bundle.needsBuild(); final bool rebuildBundle = bundle.needsBuild();
if (rebuildBundle) { if (rebuildBundle) {
...@@ -371,10 +386,10 @@ class HotRunner extends ResidentRunner { ...@@ -371,10 +386,10 @@ class HotRunner extends ResidentRunner {
await _devFS.update(progressReporter: progressReporter, await _devFS.update(progressReporter: progressReporter,
bundle: bundle, bundle: bundle,
bundleDirty: rebuildBundle, bundleDirty: rebuildBundle,
fileFilter: _startupDependencies); fileFilter: _dartDependencies);
devFSStatus.stop(showElapsedTime: true); devFSStatus.stop(showElapsedTime: true);
// Clear the minimal set after the first sync. // Clear the set after the sync.
_startupDependencies = null; _dartDependencies = null;
printTrace('Synced ${getSizeAsMB(_devFS.bytes)}.'); printTrace('Synced ${getSizeAsMB(_devFS.bytes)}.');
return true; return true;
} }
...@@ -422,10 +437,12 @@ class HotRunner extends ResidentRunner { ...@@ -422,10 +437,12 @@ class HotRunner extends ResidentRunner {
deviceAssetsDirectoryPath); deviceAssetsDirectoryPath);
} }
Future<Null> _restartFromSources() async { Future<OperationResult> _restartFromSources() async {
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService); FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start(); firstFrameTimer.start();
await _updateDevFS(); bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error');
await _launchFromDevFS(_package, _mainPath); await _launchFromDevFS(_package, _mainPath);
bool waitForFrame = bool waitForFrame =
await currentView.uiIsolate.flutterFrameworkPresent(); await currentView.uiIsolate.flutterFrameworkPresent();
...@@ -446,6 +463,7 @@ class HotRunner extends ResidentRunner { ...@@ -446,6 +463,7 @@ class HotRunner extends ResidentRunner {
flutterUsage.sendTiming('hot', 'restart', firstFrameTimer.elapsed); flutterUsage.sendTiming('hot', 'restart', firstFrameTimer.elapsed);
} }
flutterUsage.sendEvent('hot', 'restart'); flutterUsage.sendEvent('hot', 'restart');
return OperationResult.ok;
} }
/// Returns [true] if the reload was successful. /// Returns [true] if the reload was successful.
...@@ -465,8 +483,7 @@ class HotRunner extends ResidentRunner { ...@@ -465,8 +483,7 @@ class HotRunner extends ResidentRunner {
@override @override
Future<OperationResult> restart({ bool fullRestart: false, bool pauseAfterRestart: false }) async { Future<OperationResult> restart({ bool fullRestart: false, bool pauseAfterRestart: false }) async {
if (fullRestart) { if (fullRestart) {
await _restartFromSources(); return _restartFromSources();
return OperationResult.ok;
} else { } else {
return _reloadSources(pause: pauseAfterRestart); return _reloadSources(pause: pauseAfterRestart);
} }
...@@ -477,8 +494,11 @@ class HotRunner extends ResidentRunner { ...@@ -477,8 +494,11 @@ class HotRunner extends ResidentRunner {
throw 'Application isolate not found'; throw 'Application isolate not found';
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService); FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start(); firstFrameTimer.start();
if (_devFS != null) if (_devFS != null) {
await _updateDevFS(); bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error');
}
Status reloadStatus = logger.startProgress('Performing hot reload...'); Status reloadStatus = logger.startProgress('Performing hot reload...');
try { try {
Map<String, dynamic> reloadReport = Map<String, dynamic> reloadReport =
......
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