Skip to content

Commit

Permalink
[flutter_tools] initialize frontend server with build (#49405)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonahwilliams committed Feb 24, 2020
1 parent e6e79bc commit 59cc3cd
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 71 deletions.
1 change: 0 additions & 1 deletion packages/flutter_tools/lib/src/compile.dart
Expand Up @@ -469,7 +469,6 @@ abstract class ResidentCompiler {
// See: https://github.com/flutter/flutter/issues/50494
void addFileSystemRoot(String root);


/// If invoked for the first time, it compiles Dart script identified by
/// [mainPath], [invalidatedFiles] list is ignored.
/// On successive runs [invalidatedFiles] indicates which files need to be
Expand Down
3 changes: 3 additions & 0 deletions packages/flutter_tools/lib/src/devfs.dart
Expand Up @@ -488,6 +488,9 @@ class DevFS {
if (fullRestart) {
generator.reset();
}
// On a full restart, or on an initial compile for the attach based workflow,
// this will produce a full dill. Subsequent invocations will produce incremental
// dill files that depend on the invalidated files.
globals.printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files');
final CompilerOutput compilerOutput = await generator.recompile(
mainPath,
Expand Down
10 changes: 0 additions & 10 deletions packages/flutter_tools/lib/src/resident_runner.dart
Expand Up @@ -629,16 +629,6 @@ abstract class ResidentRunner {
if (!artifactDirectory.existsSync()) {
artifactDirectory.createSync(recursive: true);
}
// TODO(jonahwilliams): this is a temporary work around to regain some of
// the initialize from dill performance. Longer term, we should have a
// better way to determine where the appropriate dill file is, as this
// doesn't work for Android or macOS builds.}
if (dillOutputPath == null) {
final File existingDill = globals.fs.file(globals.fs.path.join('build', 'app.dill'));
if (existingDill.existsSync()) {
existingDill.copySync(globals.fs.path.join(artifactDirectory.path, 'app.dill'));
}
}
}

@protected
Expand Down
31 changes: 27 additions & 4 deletions packages/flutter_tools/lib/src/run_hot.dart
Expand Up @@ -16,6 +16,7 @@ import 'base/file_system.dart';
import 'base/logger.dart';
import 'base/utils.dart';
import 'build_info.dart';
import 'bundle.dart';
import 'compile.dart';
import 'convert.dart';
import 'devfs.dart';
Expand Down Expand Up @@ -329,14 +330,36 @@ class HotRunner extends ResidentRunner {

firstBuildTime = DateTime.now();

final List<Future<bool>> startupTasks = <Future<bool>>[];
for (final FlutterDevice device in flutterDevices) {
final int result = await device.runHot(
// Here we initialize the frontend_server concurrently with the platform
// build, reducing overall initialization time. This is safe because the first
// invocation of the frontend server produces a full dill file that the
// subsequent invocation in devfs will not overwrite.
if (device.generator != null) {
startupTasks.add(
device.generator.recompile(
mainPath,
<Uri>[],
outputPath: dillOutputPath ??
getDefaultApplicationKernelPath(trackWidgetCreation: device.trackWidgetCreation),
packagesFilePath : packagesFilePath,
).then((CompilerOutput output) => output?.errorCount == 0)
);
}
startupTasks.add(device.runHot(
hotRunner: this,
route: route,
);
if (result != 0) {
return result;
).then((int result) => result == 0));
}
try {
final List<bool> results = await Future.wait(startupTasks);
if (!results.every((bool passed) => passed)) {
return 1;
}
} on Exception catch (err) {
globals.printError(err.toString());
return 1;
}

return attach(
Expand Down
41 changes: 0 additions & 41 deletions packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
Expand Up @@ -84,47 +84,6 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
});

testUsingContext('Forces fast start off for devices that do not support it', () async {
final MockDevice mockDevice = MockDevice(TargetPlatform.android_arm);
when(mockDevice.name).thenReturn('mockdevice');
when(mockDevice.supportsFastStart).thenReturn(false);
when(mockDevice.supportsHotReload).thenReturn(true);
when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async => false);
when(deviceManager.hasSpecifiedAllDevices).thenReturn(false);
when(deviceManager.findTargetDevices(any)).thenAnswer((Invocation invocation) {
return Future<List<Device>>.value(<Device>[mockDevice]);
});
when(deviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Future<List<Device>>.value(<Device>[mockDevice]);
});
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();

final RunCommand command = RunCommand();
applyMocksToCommand(command);
try {
await createTestCommandRunner(command).run(<String>[
'run',
'--fast-start',
'--no-pub',
]);
fail('Expect exception');
} catch (e) {
expect(e, isA<ToolExit>());
}

final BufferLogger bufferLogger = globals.logger as BufferLogger;
expect(bufferLogger.statusText, isNot(contains(
'Using --fast-start option with device mockdevice, but this device '
'does not support it. Overriding the setting to false.'
)));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => MockDeviceManager(),
});

testUsingContext('Walks upward looking for a pubspec.yaml and succeeds if found', () async {
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages')
Expand Down
Expand Up @@ -234,10 +234,6 @@ void main() {
Usage: () => MockUsage(),
}));

test('ResidentRunner copies dill file from build output into temp directory', () => testbed.run(() async {
expect(residentRunner.artifactDirectory.childFile('app.dill').readAsStringSync(), 'ABC');
}));

test('ResidentRunner can send target platform to analytics from hot reload', () => testbed.run(() async {
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
Expand Down
22 changes: 11 additions & 11 deletions packages/flutter_tools/test/integration.shard/hot_reload_test.dart
Expand Up @@ -36,10 +36,10 @@ void main() {
});

test('newly added code executes during hot reload', () async {
await _flutter.run();
_project.uncommentHotReloadPrint();
final StringBuffer stdout = StringBuffer();
final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
await _flutter.run();
_project.uncommentHotReloadPrint();
try {
await _flutter.hotReload();
expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
Expand All @@ -49,10 +49,10 @@ void main() {
});

test('reloadMethod triggers hot reload behavior', () async {
await _flutter.run();
_project.uncommentHotReloadPrint();
final StringBuffer stdout = StringBuffer();
final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
await _flutter.run();
_project.uncommentHotReloadPrint();
try {
final String libraryId = _project.buildBreakpointUri.toString();
await _flutter.reloadMethod(libraryId: libraryId, classId: 'MyApp');
Expand All @@ -72,7 +72,6 @@ void main() {

test('breakpoints are hit after hot reload', () async {
Isolate isolate;
await _flutter.run(withDebugger: true, startPaused: true);
final Completer<void> sawTick1 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage = Completer<void>();
Expand All @@ -92,6 +91,7 @@ void main() {
}
},
);
await _flutter.run(withDebugger: true, startPaused: true);
await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
unawaited(sawTick1.future.timeout(
const Duration(seconds: 5),
Expand Down Expand Up @@ -125,16 +125,15 @@ void main() {
});

test("hot reload doesn't reassemble if paused", () async {
await _flutter.run(withDebugger: true);
final Completer<void> sawTick2 = Completer<void>();
final Completer<void> sawTick1 = Completer<void>();
final Completer<void> sawTick3 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
final StreamSubscription<String> subscription = _flutter.stdout.listen(
(String line) {
if (line.contains('((((TICK 2))))')) {
expect(sawTick2.isCompleted, isFalse);
sawTick2.complete();
if (line.contains('(((TICK 1)))')) {
expect(sawTick1.isCompleted, isFalse);
sawTick1.complete();
}
if (line.contains('The application is paused in the debugger on a breakpoint.')) {
expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
Expand All @@ -146,13 +145,14 @@ void main() {
}
},
);
await _flutter.run(withDebugger: true);
await sawTick1.future;
await _flutter.addBreakpoint(
_project.buildBreakpointUri,
_project.buildBreakpointLine,
);
bool reloaded = false;
final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
await sawTick2.future; // this should happen before it pauses
final Isolate isolate = await _flutter.waitForPause();
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
expect(reloaded, isFalse);
Expand Down

0 comments on commit 59cc3cd

Please sign in to comment.