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, {
);
}
/// 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.
String runSync(List<String> cmd, {
String workingDirectory,
......@@ -187,6 +197,7 @@ void _traceCommand(List<String> args, { String workingDirectory }) {
String _runWithLoggingSync(List<String> cmd, {
bool checked: false,
bool noisyErrors: false,
bool throwStandardErrorOnError: false,
String workingDirectory,
bool allowReentrantFlutter: false,
bool hideStdout: false,
......@@ -216,6 +227,9 @@ String _runWithLoggingSync(List<String> cmd, {
printTrace(results.stderr.trim());
}
if (throwStandardErrorOnError)
throw results.stderr.trim();
if (checked)
throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}';
}
......
......@@ -38,9 +38,9 @@ String getDevFSLoaderScript() {
'loader_app.dart'));
}
class StartupDependencySetBuilder {
StartupDependencySetBuilder(this.mainScriptPath,
this.projectRootPath);
class DartDependencySetBuilder {
DartDependencySetBuilder(this.mainScriptPath,
this.projectRootPath);
final String mainScriptPath;
final String projectRootPath;
......@@ -56,12 +56,7 @@ class StartupDependencySetBuilder {
mainScriptPath
];
String output;
try {
output = runCheckedSync(args, hideStdout: true);
} catch (e) {
return null;
}
String output = runSyncAndThrowStdErrOnError(args);
final List<String> lines = LineSplitter.split(output).toList();
final Set<String> minimalDependencies = new Set<String>();
......@@ -135,7 +130,7 @@ class HotRunner extends ResidentRunner {
ApplicationPackage _package;
String _mainPath;
String _projectRootPath;
Set<String> _startupDependencies;
Set<String> _dartDependencies;
int _observatoryPort;
final AssetBundle bundle = new AssetBundle();
final bool benchmarkMode;
......@@ -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({
Completer<DebugConnectionInfo> connectionInfoCompleter,
String route,
......@@ -184,6 +196,12 @@ class HotRunner extends ResidentRunner {
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.
if (shouldBuild && device is AndroidDevice) {
printTrace('Running build command.');
......@@ -231,15 +249,6 @@ class HotRunner extends ResidentRunner {
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;
if (!result.started) {
......@@ -295,10 +304,6 @@ class HotRunner extends ResidentRunner {
registerSignalHandlers();
printTrace('Finishing file synchronization');
// Finish the file sync now.
await _updateDevFS();
if (benchmarkMode) {
// We are running in benchmark mode.
printStatus('Running in benchmark mode.');
......@@ -325,13 +330,19 @@ class HotRunner extends ResidentRunner {
Future<Null> handleTerminalCommand(String code) async {
final String lower = code.toLowerCase();
if ((lower == 'r') || (code == AnsiTerminal.KEY_F5)) {
OperationResult result = OperationResult.ok;
// F5, restart
if ((code == 'r') || (code == AnsiTerminal.KEY_F5)) {
// lower-case 'r'
await _reloadSources();
result = await _reloadSources();
} else {
// 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 {
}
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...');
final bool rebuildBundle = bundle.needsBuild();
if (rebuildBundle) {
......@@ -371,10 +386,10 @@ class HotRunner extends ResidentRunner {
await _devFS.update(progressReporter: progressReporter,
bundle: bundle,
bundleDirty: rebuildBundle,
fileFilter: _startupDependencies);
fileFilter: _dartDependencies);
devFSStatus.stop(showElapsedTime: true);
// Clear the minimal set after the first sync.
_startupDependencies = null;
// Clear the set after the sync.
_dartDependencies = null;
printTrace('Synced ${getSizeAsMB(_devFS.bytes)}.');
return true;
}
......@@ -422,10 +437,12 @@ class HotRunner extends ResidentRunner {
deviceAssetsDirectoryPath);
}
Future<Null> _restartFromSources() async {
Future<OperationResult> _restartFromSources() async {
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start();
await _updateDevFS();
bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error');
await _launchFromDevFS(_package, _mainPath);
bool waitForFrame =
await currentView.uiIsolate.flutterFrameworkPresent();
......@@ -446,6 +463,7 @@ class HotRunner extends ResidentRunner {
flutterUsage.sendTiming('hot', 'restart', firstFrameTimer.elapsed);
}
flutterUsage.sendEvent('hot', 'restart');
return OperationResult.ok;
}
/// Returns [true] if the reload was successful.
......@@ -465,8 +483,7 @@ class HotRunner extends ResidentRunner {
@override
Future<OperationResult> restart({ bool fullRestart: false, bool pauseAfterRestart: false }) async {
if (fullRestart) {
await _restartFromSources();
return OperationResult.ok;
return _restartFromSources();
} else {
return _reloadSources(pause: pauseAfterRestart);
}
......@@ -477,8 +494,11 @@ class HotRunner extends ResidentRunner {
throw 'Application isolate not found';
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start();
if (_devFS != null)
await _updateDevFS();
if (_devFS != null) {
bool updatedDevFS = await _updateDevFS();
if (!updatedDevFS)
return new OperationResult(1, 'Dart Source Error');
}
Status reloadStatus = logger.startProgress('Performing hot reload...');
try {
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