Commit 4fe10dbf authored by Ian Hickson's avatar Ian Hickson

Refactor listen in terms of start.

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