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.
|
/// 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 }) {
|
|||||||
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, {
|
|||||||
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() {
|
|||||||
'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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
@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 {
|
|||||||
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 =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user