Commit 81b4e827 authored by John McCutchan's avatar John McCutchan Committed by GitHub

Split hot run mode into a separate file (#5233)

parent ea7d5bf2
......@@ -52,10 +52,27 @@ class AssetBundle {
static const String _kFontSetMaterial = 'material';
static const String _kFontSetRoboto = 'roboto';
DateTime _lastBuildTimestamp;
bool needsBuild({String manifestPath: defaultManifestPath}) {
if (_lastBuildTimestamp == null)
return true;
FileStat stat = new File(manifestPath).statSync();
if (stat.type == FileSystemEntityType.NOT_FOUND)
return true;
return stat.modified.isAfter(_lastBuildTimestamp);
}
Future<int> build({String manifestPath: defaultManifestPath,
String workingDirPath: defaultWorkingDirPath,
bool includeRobotoFonts: true}) async {
Object manifest = _loadFlutterYamlManifest(manifestPath);
if (manifest == null) {
// No manifest file found for this application.
return 0;
}
if (manifest != null) {
int result = await _validateFlutterYamlManifest(manifest);
if (result != 0)
......@@ -65,6 +82,8 @@ class AssetBundle {
assert(manifestDescriptor != null);
String assetBasePath = path.dirname(path.absolute(manifestPath));
_lastBuildTimestamp = new DateTime.now();
final PackageMap packageMap =
new PackageMap(path.join(assetBasePath, '.packages'));
......
......@@ -13,7 +13,7 @@ import '../base/utils.dart';
import '../build_info.dart';
import '../dart/package_map.dart';
import '../globals.dart';
import '../run.dart';
import '../resident_runner.dart';
import 'build.dart';
const String _kDefaultAotOutputDir = 'build/aot';
......
......@@ -18,7 +18,7 @@ import '../base/utils.dart';
import '../build_info.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../run.dart';
import '../resident_runner.dart';
import '../services.dart';
import 'build_aot.dart';
import 'build.dart';
......
......@@ -291,7 +291,7 @@ class AppDomain extends Domain {
String route = _getStringArg(args, 'route');
String mode = _getStringArg(args, 'mode');
String target = _getStringArg(args, 'target');
bool hotMode = _getBoolArg(args, 'hot');
// TODO(johnmccutchan): Wire up support for hot mode.
Device device = daemon.deviceDomain._getDevice(deviceId);
if (device == null)
......@@ -323,8 +323,7 @@ class AppDomain extends Domain {
device,
target: target,
debuggingOptions: options,
usesTerminalUI: false,
hotMode: hotMode
usesTerminalUI: false
);
AppInstance app = new AppInstance(_getNextAppId(), runner);
......
......@@ -21,7 +21,7 @@ import '../dart/sdk.dart';
import '../device.dart';
import '../globals.dart';
import '../ios/simulators.dart' show SimControl, IOSSimulatorUtils;
import '../run.dart';
import '../resident_runner.dart';
import 'build_apk.dart' as build_apk;
import 'run.dart';
......
......@@ -12,7 +12,9 @@ import '../build_info.dart';
import '../cache.dart';
import '../device.dart';
import '../globals.dart';
import '../hot.dart' as hot;
import '../observatory.dart';
import '../resident_runner.dart';
import '../run.dart';
import '../runner/flutter_command.dart';
import 'build_apk.dart';
......@@ -132,18 +134,25 @@ class RunCommand extends RunCommandBase {
}
if (argResults['resident']) {
RunAndStayResident runner = new RunAndStayResident(
deviceForCommand,
target: targetFile,
debuggingOptions: options,
hotMode: argResults['hot']
);
return runner.run(
traceStartup: traceStartup,
benchmark: argResults['benchmark'],
route: route
);
if (argResults['hot']) {
hot.HotRunner runner = new hot.HotRunner(
deviceForCommand,
target: targetFile,
debuggingOptions: options
);
return runner.run(route: route);
} else {
RunAndStayResident runner = new RunAndStayResident(
deviceForCommand,
target: targetFile,
debuggingOptions: options
);
return runner.run(
traceStartup: traceStartup,
benchmark: argResults['benchmark'],
route: route
);
}
} else {
// TODO(devoncarew): Remove this path and support the `--no-resident` option
// using the `RunAndStayResident` class.
......
......@@ -12,7 +12,7 @@ import '../build_info.dart';
import '../cache.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../run.dart';
import '../resident_runner.dart';
import '../runner/flutter_command.dart';
const String _kDefaultBundlePath = 'build/app.flx';
......
......@@ -180,7 +180,9 @@ class DevFS {
return await _operations.destroy(fsName);
}
Future<dynamic> update({ DevFSProgressReporter progressReporter, AssetBundle bundle }) async {
Future<dynamic> update({ DevFSProgressReporter progressReporter,
AssetBundle bundle,
bool bundleDirty: false }) async {
_bytes = 0;
// Mark all entries as not seen.
_entries.forEach((String path, DevFSEntry entry) {
......@@ -189,7 +191,8 @@ class DevFS {
printTrace('DevFS: Starting sync from $rootDirectory');
// Send the root and lib directories.
Directory directory = rootDirectory;
_syncDirectory(directory, recursive: true);
await _syncDirectory(directory, recursive: true);
printTrace('DevFS: Syncing of $rootDirectory finished');
String packagesFilePath = path.join(rootDirectory.path, kPackagesFileName);
StringBuffer sb;
// Send the packages.
......@@ -202,12 +205,16 @@ class DevFS {
if (uri.toString() == 'lib/')
continue;
Directory directory = new Directory.fromUri(uri);
if (_syncDirectory(directory,
directoryName: 'packages/$packageName',
recursive: true)) {
printTrace('DevFS: Syncing package $packageName started');
bool packageWritten =
await _syncDirectory(directory,
directoryName: 'packages/$packageName',
recursive: true);
if (packageWritten) {
sb ??= new StringBuffer();
sb.writeln('$packageName:packages/$packageName');
}
printTrace('DevFS: Syncing package $packageName finished');
}
}
if (bundle != null) {
......@@ -216,6 +223,11 @@ class DevFS {
// We write the assets into 'build/flx' so that they are in the
// same location in DevFS and the iOS simulator.
final String devicePath = path.join('build/flx', entry.archivePath);
if (!bundleDirty && entry.isStringEntry) {
// When the bundle isn't dirty, we do not need to sync string
// entries.
continue;
}
_syncBundleEntry(devicePath, entry);
}
}
......@@ -309,10 +321,10 @@ class DevFS {
return false;
}
bool _syncDirectory(Directory directory,
{String directoryName,
bool recursive: false,
bool ignoreDotFiles: true}) {
Future<bool> _syncDirectory(Directory directory,
{String directoryName,
bool recursive: false,
bool ignoreDotFiles: true}) async {
String prefix = directoryName;
if (prefix == null) {
prefix = path.relative(directory.path, from: rootDirectory.path);
......@@ -320,9 +332,9 @@ class DevFS {
prefix = '';
}
try {
List<FileSystemEntity> files =
directory.listSync(recursive: recursive, followLinks: false);
for (FileSystemEntity file in files) {
Stream<FileSystemEntity> files =
directory.list(recursive: recursive, followLinks: false);
await for (FileSystemEntity file in files) {
if (file is! File) {
// Skip non-files.
continue;
......
This diff is collapsed.
......@@ -116,16 +116,37 @@ class Observatory {
});
}
Future<Null> reloadSources(String isolateId) async {
Future<Map<String, dynamic>> reloadSources(String isolateId) async {
try {
await sendRequest('_reloadSources',
<String, dynamic>{ 'isolateId': isolateId });
return null;
Response response =
await sendRequest('_reloadSources',
<String, dynamic>{ 'isolateId': isolateId });
return response.response;
} catch (e) {
return new Future<Null>.error(e.data['details']);
return new Future<Map<String, dynamic>>.error(e.data['details']);
}
}
Future<String> getFirstViewId() async {
Map<String, dynamic> response = await peer.sendRequest('_flutter.listViews');
List<Map<String, String>> views = response['views'];
return views[0]['id'];
}
Future<Null> runInView(String viewId,
String main,
String packages,
String assetsDirectory) async {
await peer.sendRequest('_flutter.runInView',
<String, dynamic> {
'viewId': viewId,
'mainScript': main,
'packagesFile': packages,
'assetDirectory': assetsDirectory
});
return null;
}
Future<Response> clearVMTimeline() => sendRequest('_clearVMTimeline');
Future<Response> setVMTimelineFlags(List<String> recordedStreams) {
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'base/logger.dart';
import 'build_info.dart';
import 'device.dart';
import 'globals.dart';
import 'observatory.dart';
// Shared code between different resident application runners.
abstract class ResidentRunner {
ResidentRunner(this.device, {
this.target,
this.debuggingOptions,
this.usesTerminalUI: true
});
final Device device;
final String target;
final DebuggingOptions debuggingOptions;
final bool usesTerminalUI;
final Completer<int> _finished = new Completer<int>();
Observatory serviceProtocol;
StreamSubscription<String> _loggingSubscription;
Future<Null> stop() async {
await stopEchoingDeviceLog();
return stopApp();
}
void _debugDumpApp() {
serviceProtocol.flutterDebugDumpApp(serviceProtocol.firstIsolateId);
}
void _debugDumpRenderTree() {
serviceProtocol.flutterDebugDumpRenderTree(serviceProtocol.firstIsolateId);
}
void registerSignalHandlers() {
ProcessSignal.SIGINT.watch().listen((ProcessSignal signal) async {
_resetTerminal();
await cleanupAfterSignal();
exit(0);
});
ProcessSignal.SIGTERM.watch().listen((ProcessSignal signal) async {
_resetTerminal();
await cleanupAfterSignal();
exit(0);
});
}
Future<Null> startEchoingDeviceLog() async {
if (_loggingSubscription != null) {
return;
}
_loggingSubscription = device.logReader.logLines.listen((String line) {
if (!line.contains('Observatory listening on http') &&
!line.contains('Diagnostic server listening on http'))
printStatus(line);
});
}
Future<Null> stopEchoingDeviceLog() async {
if (_loggingSubscription != null) {
await _loggingSubscription.cancel();
}
_loggingSubscription = null;
}
Future<Null> connectToServiceProtocol(int port) async {
if (!debuggingOptions.debuggingEnabled) {
return new Future<Null>.error('Error the service protocol is not enabled.');
}
serviceProtocol = await Observatory.connect(port);
printTrace('Connected to service protocol on port $port');
serviceProtocol.populateIsolateInfo();
serviceProtocol.onExtensionEvent.listen((Event event) {
printTrace(event.toString());
});
serviceProtocol.onIsolateEvent.listen((Event event) {
printTrace(event.toString());
});
// Listen for service protocol connection to close.
serviceProtocol.done.whenComplete(() {
appFinished();
});
}
/// Returns [true] if the input has been handled by this function.
bool _commonTerminalInputHandler(String character) {
final String lower = character.toLowerCase();
printStatus(''); // the key the user tapped might be on this line
if (lower == 'h' || lower == '?' || character == AnsiTerminal.KEY_F1) {
// F1, help
printHelp();
return true;
} else if (lower == 'w') {
_debugDumpApp();
return true;
} else if (lower == 't') {
_debugDumpRenderTree();
return true;
} else if (lower == 'q' || character == AnsiTerminal.KEY_F10) {
// F10, exit
stopApp();
return true;
}
return false;
}
void appFinished() {
if (_finished.isCompleted)
return;
printStatus('Application finished.');
_resetTerminal();
_finished.complete(0);
}
void _resetTerminal() {
if (usesTerminalUI)
terminal.singleCharMode = false;
}
void setupTerminal() {
if (usesTerminalUI) {
if (!logger.quiet)
printHelp();
terminal.singleCharMode = true;
terminal.onCharInput.listen((String code) {
if (!_commonTerminalInputHandler(code)) {
handleTerminalCommand(code);
}
});
}
}
Future<int> waitForAppToFinish() async {
int exitCode = await _finished.future;
await cleanupAtFinish();
return exitCode;
}
Future<Null> stopApp() {
if (serviceProtocol != null && !serviceProtocol.isClosed) {
if (serviceProtocol.isolates.isNotEmpty) {
serviceProtocol.flutterExit(serviceProtocol.firstIsolateId);
return new Future<Null>.delayed(new Duration(milliseconds: 100));
}
}
appFinished();
return new Future<Null>.value();
}
/// Called when a signal has requested we exit.
Future<Null> cleanupAfterSignal();
/// Called right before we exit.
Future<Null> cleanupAtFinish();
/// Called to print help to the terminal.
void printHelp();
/// Called when the runner should handle a terminal command.
void handleTerminalCommand(String code);
}
/// Given the value of the --target option, return the path of the Dart file
/// where the app's main function should be.
String findMainDartFile([String target]) {
if (target == null)
target = '';
String targetPath = path.absolute(target);
if (FileSystemEntity.isDirectorySync(targetPath))
return path.join(targetPath, 'lib', 'main.dart');
else
return targetPath;
}
String getMissingPackageHintForPlatform(TargetPlatform platform) {
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
return 'Is your project missing an android/AndroidManifest.xml?\nConsider running "flutter create ." to create one.';
case TargetPlatform.ios:
return 'Is your project missing an ios/Runner/Info.plist?\nConsider running "flutter create ." to create one.';
default:
return null;
}
}
This diff is collapsed.
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