Commit 1d3e7515 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #1037 from Hixie/listen

Refactor listen in terms of start.
parents 73102aec 4fe10dbf
......@@ -62,12 +62,14 @@ Future<Process> runDetached(List<String> cmd) {
/// Run cmd and return stdout.
/// Throws an error if cmd exits with a non-zero value.
String runCheckedSync(List<String> cmd, { String workingDirectory }) =>
_runWithLoggingSync(cmd, workingDirectory: workingDirectory, checked: true);
String runCheckedSync(List<String> cmd, { String workingDirectory }) {
return _runWithLoggingSync(cmd, workingDirectory: workingDirectory, checked: true);
}
/// Run cmd and return stdout.
String runSync(List<String> cmd, { String workingDirectory }) =>
_runWithLoggingSync(cmd, workingDirectory: workingDirectory);
String runSync(List<String> cmd, { String workingDirectory }) {
return _runWithLoggingSync(cmd, workingDirectory: workingDirectory);
}
/// Return the platform specific name for the given Dart SDK binary. So, `pub`
/// ==> `pub.bat`.
......
......@@ -252,7 +252,7 @@ class ApkCommand extends FlutterCommand {
await downloadToolchain();
// Find the path to the main Dart file.
String mainPath = StartCommand.findMainDartFile(argResults['target']);
String mainPath = StartCommandBase.findMainDartFile(argResults['target']);
// Build the FLX.
BuildCommand builder = new BuildCommand();
......
......@@ -5,104 +5,67 @@
import 'dart:async';
import 'dart:io';
import '../application_package.dart';
import '../base/logging.dart';
import '../base/process.dart';
import '../device.dart';
import '../runner/flutter_command.dart';
import 'build.dart';
import 'start.dart';
class ListenCommand extends FlutterCommand {
class ListenCommand extends StartCommandBase {
final String name = 'listen';
final String description = 'Listen for changes to files and reload the running app on all connected devices.';
List<String> watchCommand;
final String description = 'Listen for changes to files and reload the running app on all connected devices. (Android only.)'
'By default, only listens to "./" and "./lib/". To listen to additional directories, list them on the command line.';
/// Only run once. Used for testing.
bool singleRun;
final bool singleRun;
ListenCommand({ this.singleRun: false }) {
argParser.addFlag('checked',
negatable: true,
defaultsTo: true,
help: 'Toggle Dart\'s checked mode.');
argParser.addOption('target',
defaultsTo: '.',
abbr: 't',
help: 'Target app path or filename to start.');
}
static const String _remoteFlutterBundle = 'Documents/app.flx';
ListenCommand({ this.singleRun: false });
@override
Future<int> runInProject() async {
await downloadApplicationPackagesAndConnectToDevices();
await downloadToolchain();
if (argResults.rest.length > 0) {
watchCommand = _initWatchCommand(argResults.rest);
} else {
watchCommand = _initWatchCommand(['.']);
}
List<String> watchCommand = _constructWatchCommand(() sync* {
yield* argResults.rest;
yield '.';
yield 'lib';
}());
while (true) {
int result = 0;
bool firstTime = true;
do {
logging.info('Updating running Flutter apps...');
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
await builder.buildInTempDir(
onBundleAvailable: (String localBundlePath) {
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected())
continue;
if (device is AndroidDevice) {
device.startBundle(package, localBundlePath, poke: true, checked: argResults['checked']);
} else if (device is IOSDevice) {
device.pushFile(package, localBundlePath, _remoteFlutterBundle);
} else if (device is IOSSimulator) {
// TODO(abarth): Move pushFile up to Device once Android supports
// pushing new bundles.
device.pushFile(package, localBundlePath, _remoteFlutterBundle);
} else {
assert(false);
}
}
}
);
if (singleRun || !watchDirectory())
break;
}
result = await startApp(install: firstTime, stop: true);
firstTime = false;
} while (!singleRun && result == 0 && _watchDirectory(watchCommand));
return 0;
}
List<String> _initWatchCommand(List<String> directories) {
List<String> _constructWatchCommand(Iterable<String> directories) {
if (Platform.isMacOS) {
try {
runCheckedSync(['which', 'fswatch']);
runCheckedSync(<String>['which', 'fswatch']);
} catch (e) {
logging.severe('"listen" command is only useful if you have installed '
'fswatch on Mac. Run "brew install fswatch" to install it with '
'homebrew.');
return null;
}
return ['fswatch', '-r', '-v', '-1']..addAll(directories);
return <String>['fswatch', '-r', '-v', '-1']..addAll(directories);
} else if (Platform.isLinux) {
try {
runCheckedSync(['which', 'inotifywait']);
runCheckedSync(<String>['which', 'inotifywait']);
} catch (e) {
logging.severe('"listen" command is only useful if you have installed '
'inotifywait on Linux. Run "apt-get install inotify-tools" or '
'equivalent to install it.');
return null;
}
return [
return <String>[
'inotifywait',
'-r',
'-e',
// Only listen for events that matter, to avoid triggering constantly
// from the editor watching files
// from the editor watching files.
'modify,close_write,move,create,delete',
]..addAll(directories);
} else {
......@@ -111,17 +74,15 @@ class ListenCommand extends FlutterCommand {
return null;
}
bool watchDirectory() {
if (watchCommand == null)
return false;
bool _watchDirectory(List<String> watchCommand) {
logging.info('Attempting to listen to these directories: ${watchCommand.join(", ")}');
assert(watchCommand != null);
try {
runCheckedSync(watchCommand);
} catch (e) {
logging.warning('Watching directories failed.', e);
return false;
}
return true;
}
}
......@@ -154,7 +154,7 @@ class RunMojoCommand extends FlutterCommand {
if (bundlePath == null) {
bundlePath = _kDefaultBundlePath;
String mainPath = StartCommand.findMainDartFile(argResults['target']);
String mainPath = StartCommandBase.findMainDartFile(argResults['target']);
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
......
......@@ -15,14 +15,10 @@ import 'build.dart';
import 'install.dart';
import 'stop.dart';
class StartCommand extends FlutterCommand {
final String name = 'start';
final String description = 'Start your Flutter app on attached devices.';
// We don't yet support iOS here. https://github.com/flutter/flutter/issues/1036
StartCommand() {
argParser.addFlag('poke',
negatable: false,
help: 'Restart the connection to the server (Android only).');
abstract class StartCommandBase extends FlutterCommand {
StartCommandBase() {
argParser.addFlag('checked',
negatable: true,
defaultsTo: true,
......@@ -35,9 +31,8 @@ class StartCommand extends FlutterCommand {
defaultsTo: '',
abbr: 't',
help: 'Target app path or filename to start.');
argParser.addOption('route', help: 'Which route to load when starting the app.');
argParser.addFlag('boot',
help: 'Boot the iOS Simulator if it isn\'t already running.');
argParser.addOption('route',
help: 'Which route to load when starting the app.');
}
/// Given the value of the --target option, return the path of the Dart file
......@@ -51,29 +46,29 @@ class StartCommand extends FlutterCommand {
}
}
@override
Future<int> runInProject() async {
logging.fine('downloading toolchain');
await Future.wait([
downloadToolchain(),
downloadApplicationPackagesAndConnectToDevices(),
]);
Future<int> startApp({ bool stop: true, bool install: true, bool poke: false }) async {
bool poke = argResults['poke'];
if (!poke) {
logging.fine('running stop command');
String mainPath = findMainDartFile(argResults['target']);
if (!FileSystemEntity.isFileSync(mainPath)) {
String message = 'Tried to run $mainPath, but that file does not exist.';
if (!argResults.wasParsed('target'))
message += '\nConsider using the -t option to specify that Dart file to start.';
logging.severe(message);
return 1;
}
if (stop) {
logging.fine('Running stop command.');
StopCommand stopper = new StopCommand();
stopper.inheritFromParent(this);
stopper.stop();
logging.fine('running install command');
// Only install if the user did not specify a poke
}
if (install) {
logging.fine('Running install command.');
InstallCommand installer = new InstallCommand();
installer.inheritFromParent(this);
installer.install(boot: argResults['boot']);
installer.install();
}
bool startedSomething = false;
......@@ -83,39 +78,22 @@ class StartCommand extends FlutterCommand {
if (package == null || !device.isConnected())
continue;
if (device is AndroidDevice) {
String mainPath = findMainDartFile(argResults['target']);
if (!FileSystemEntity.isFileSync(mainPath)) {
String message = 'Tried to run $mainPath, but that file does not exist.';
if (!argResults.wasParsed('target'))
message += '\nConsider using the -t option to specify that Dart file to start.';
stderr.writeln(message);
continue;
logging.fine('Running build command for $device.');
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
await builder.buildInTempDir(
mainPath: mainPath,
onBundleAvailable: (String localBundlePath) {
logging.fine('Starting bundle for $device.');
final AndroidDevice androidDevice = device; // https://github.com/flutter/flutter/issues/1035
if (androidDevice.startBundle(package, localBundlePath,
poke: poke,
checked: argResults['checked'],
traceStartup: argResults['trace-startup'],
route: argResults['route']))
startedSomething = true;
}
logging.fine('running build command for $device');
BuildCommand builder = new BuildCommand();
builder.inheritFromParent(this);
await builder.buildInTempDir(
mainPath: mainPath,
onBundleAvailable: (String localBundlePath) {
logging.fine('running start bundle for $device');
if (device.startBundle(package, localBundlePath,
poke: poke,
checked: argResults['checked'],
traceStartup: argResults['trace-startup'],
route: argResults['route']))
startedSomething = true;
}
);
} else {
logging.fine('running start command for $device');
if (await device.startApp(package))
startedSomething = true;
}
);
}
if (!startedSomething) {
......@@ -126,8 +104,35 @@ class StartCommand extends FlutterCommand {
}
}
logging.fine('finished start command');
return startedSomething ? 0 : 2;
}
}
class StartCommand extends StartCommandBase {
final String name = 'start';
final String description = 'Start your Flutter app on attached devices. (Android only.)';
StartCommand() {
argParser.addFlag('poke',
negatable: false,
help: 'Restart the connection to the server.');
}
@override
Future<int> runInProject() async {
logging.fine('Downloading toolchain.');
await Future.wait([
downloadToolchain(),
downloadApplicationPackagesAndConnectToDevices(),
]);
bool poke = argResults['poke'];
// Only stop and reinstall if the user did not specify a poke
int result = await startApp(stop: !poke, install: !poke, poke: poke);
logging.fine('Finished start command.');
return result;
}
}
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