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.
This commit is contained in:
parent
2d9d4c2d3a
commit
594c4f998a
@ -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 =
|
||||
|
Loading…
x
Reference in New Issue
Block a user