Commit 5daf58ce authored by Devon Carew's avatar Devon Carew

refactor commands to move out re-usable functionality

parent 5776c6e3
...@@ -302,7 +302,7 @@ class ApkCommand extends FlutterCommand { ...@@ -302,7 +302,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 = StartCommandBase.findMainDartFile(argResults['target']); String mainPath = findMainDartFile(argResults['target']);
// Build the FLX. // Build the FLX.
int result; int result;
......
...@@ -11,10 +11,12 @@ import '../base/logging.dart'; ...@@ -11,10 +11,12 @@ import '../base/logging.dart';
import '../device.dart'; import '../device.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import 'start.dart'; import 'start.dart';
import 'stop.dart'; import 'stop.dart' as stop;
const String protocolVersion = '0.0.2'; const String protocolVersion = '0.0.2';
// TODO(devoncarew): Pass logging data back to the client.
/// A server process command. This command will start up a long-lived server. /// A server process command. This command will start up a long-lived server.
/// It reads JSON-RPC based commands from stdin, executes them, and returns /// It reads JSON-RPC based commands from stdin, executes them, and returns
/// JSON-RPC based responses and events to stdout. /// JSON-RPC based responses and events to stdout.
...@@ -132,6 +134,8 @@ abstract class Domain { ...@@ -132,6 +134,8 @@ abstract class Domain {
_handlers[name] = handler; _handlers[name] = handler;
} }
FlutterCommand get command => daemon.daemonCommand;
String toString() => name; String toString() => name;
void handleCommand(String name, dynamic id, dynamic args) { void handleCommand(String name, dynamic id, dynamic args) {
...@@ -190,17 +194,23 @@ class AppDomain extends Domain { ...@@ -190,17 +194,23 @@ class AppDomain extends Domain {
registerHandler('stopAll', stopAll); registerHandler('stopAll', stopAll);
} }
Future<dynamic> start(dynamic args) { Future<dynamic> start(dynamic args) async {
// TODO: Add the ability to pass args: target, http, checked // TODO: Add the ability to pass args: target, http, checked
StartCommand startComand = new StartCommand();
startComand.inheritFromParent(daemon.daemonCommand); await Future.wait([
return startComand.runInProject().then((_) => null); command.downloadToolchain(),
command.downloadApplicationPackagesAndConnectToDevices(),
], eagerError: true);
return startApp(
command.devices,
command.applicationPackages,
command.toolchain
).then((int result) => null);
} }
Future<bool> stopAll(dynamic args) { Future<bool> stopAll(dynamic args) {
StopCommand stopCommand = new StopCommand(); return stop.stopAll(command.devices, command.applicationPackages);
stopCommand.inheritFromParent(daemon.daemonCommand);
return stopCommand.stop();
} }
} }
......
...@@ -13,30 +13,38 @@ class InstallCommand extends FlutterCommand { ...@@ -13,30 +13,38 @@ class InstallCommand extends FlutterCommand {
final String description = 'Install Flutter apps on attached devices.'; final String description = 'Install Flutter apps on attached devices.';
InstallCommand() { InstallCommand() {
argParser.addFlag('boot', argParser.addFlag('boot', help: 'Boot the iOS Simulator if it isn\'t already running.');
help: 'Boot the iOS Simulator if it isn\'t already running.');
} }
@override @override
Future<int> runInProject() async { Future<int> runInProject() async {
await downloadApplicationPackagesAndConnectToDevices(); await downloadApplicationPackagesAndConnectToDevices();
return install(boot: argResults['boot']) ? 0 : 2; bool installedAny = installApp(
devices,
applicationPackages,
boot: argResults['boot']
);
return installedAny ? 0 : 2;
} }
}
bool install({ bool boot: false }) { bool installApp(
if (boot) DeviceStore devices,
devices.iOSSimulator?.boot(); ApplicationPackageStore applicationPackages, {
bool boot: false
bool installedSomewhere = false; }) {
if (boot)
for (Device device in devices.all) { devices.iOSSimulator?.boot();
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected() || device.isAppInstalled(package)) bool installedSomewhere = false;
continue;
if (device.installApp(package)) for (Device device in devices.all) {
installedSomewhere = true; ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
} if (package == null || !device.isConnected() || device.isAppInstalled(package))
continue;
return installedSomewhere; if (device.installApp(package))
installedSomewhere = true;
} }
return installedSomewhere;
} }
...@@ -18,6 +18,8 @@ class ListCommand extends FlutterCommand { ...@@ -18,6 +18,8 @@ class ListCommand extends FlutterCommand {
help: 'Log additional details about attached devices.'); help: 'Log additional details about attached devices.');
} }
bool get requiresProjectRoot => false;
@override @override
Future<int> runInProject() async { Future<int> runInProject() async {
connectToDevices(); connectToDevices();
......
...@@ -34,7 +34,17 @@ class ListenCommand extends StartCommandBase { ...@@ -34,7 +34,17 @@ class ListenCommand extends StartCommandBase {
bool firstTime = true; bool firstTime = true;
do { do {
logging.info('Updating running Flutter apps...'); logging.info('Updating running Flutter apps...');
result = await startApp(install: firstTime, stop: true); result = await startApp(
devices,
applicationPackages,
toolchain,
target: argResults['target'],
install: firstTime,
stop: true,
checked: argResults['checked'],
traceStartup: argResults['trace-startup'],
route: argResults['route']
);
firstTime = false; firstTime = false;
} while (!singleRun && result == 0 && _watchDirectory(watchCommand)); } while (!singleRun && result == 0 && _watchDirectory(watchCommand));
return 0; return 0;
......
...@@ -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 = StartCommandBase.findMainDartFile(argResults['target']); String mainPath = findMainDartFile(argResults['target']);
int result = await flx.build( int result = await flx.build(
toolchain, toolchain,
......
...@@ -13,9 +13,23 @@ import '../build_configuration.dart'; ...@@ -13,9 +13,23 @@ import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
import '../flx.dart' as flx; import '../flx.dart' as flx;
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../toolchain.dart';
import 'install.dart'; import 'install.dart';
import 'stop.dart'; import 'stop.dart';
/// 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;
}
}
// We don't yet support iOS here. https://github.com/flutter/flutter/issues/1036 // We don't yet support iOS here. https://github.com/flutter/flutter/issues/1036
abstract class StartCommandBase extends FlutterCommand { abstract class StartCommandBase extends FlutterCommand {
...@@ -29,101 +43,11 @@ abstract class StartCommandBase extends FlutterCommand { ...@@ -29,101 +43,11 @@ abstract class StartCommandBase extends FlutterCommand {
defaultsTo: false, defaultsTo: false,
help: 'Start tracing during startup.'); help: 'Start tracing during startup.');
argParser.addOption('target', argParser.addOption('target',
defaultsTo: '',
abbr: 't', abbr: 't',
help: 'Target app path or filename to start.'); help: 'Target app path or filename to start.');
argParser.addOption('route', argParser.addOption('route',
help: 'Which route to load when starting the app.'); help: 'Which route to load when starting the app.');
} }
/// Given the value of the --target option, return the path of the Dart file
/// where the app's main function should be.
static String findMainDartFile(String target) {
String targetPath = path.absolute(target);
if (FileSystemEntity.isDirectorySync(targetPath)) {
return path.join(targetPath, 'lib', 'main.dart');
} else {
return targetPath;
}
}
Future<int> startApp({
bool stop: true,
bool install: true,
bool poke: false,
bool clearLogs: false
}) async {
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();
}
if (install) {
logging.fine('Running install command.');
InstallCommand installer = new InstallCommand();
installer.inheritFromParent(this);
installer.install();
}
bool startedSomething = false;
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected())
continue;
logging.fine('Running build command for $device.');
if (device.platform == TargetPlatform.android) {
await flx.buildInTempDir(
toolchain,
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'],
clearLogs: clearLogs
)) {
startedSomething = true;
}
}
);
} else {
bool result = await device.startApp(package);
if (!result) {
logging.severe('Could not start \'${package.name}\' on \'${device.id}\'');
} else {
startedSomething = true;
}
}
}
if (!startedSomething) {
if (!devices.all.any((device) => device.isConnected())) {
logging.severe('Unable to run application - no connected devices.');
} else {
logging.severe('Unable to run application.');
}
}
return startedSomething ? 0 : 2;
}
} }
class StartCommand extends StartCommandBase { class StartCommand extends StartCommandBase {
...@@ -152,10 +76,103 @@ class StartCommand extends StartCommandBase { ...@@ -152,10 +76,103 @@ class StartCommand extends StartCommandBase {
bool poke = argResults['poke']; bool poke = argResults['poke'];
bool clearLogs = argResults['clear-logs']; bool clearLogs = argResults['clear-logs'];
// Only stop and reinstall if the user did not specify a poke // Only stop and reinstall if the user did not specify a poke.
int result = await startApp(stop: !poke, install: !poke, poke: poke, clearLogs: clearLogs); int result = await startApp(
devices,
applicationPackages,
toolchain,
target: argResults['target'],
install: !poke,
stop: !poke,
checked: argResults['checked'],
traceStartup: argResults['trace-startup'],
route: argResults['route'],
poke: poke,
clearLogs: clearLogs
);
logging.fine('Finished start command.'); logging.fine('Finished start command.');
return result; return result;
} }
} }
Future<int> startApp(
DeviceStore devices,
ApplicationPackageStore applicationPackages,
Toolchain toolchain, {
String target,
bool stop: true,
bool install: true,
bool checked: true,
bool traceStartup: false,
String route,
bool poke: false,
bool clearLogs: false
}) async {
String mainPath = findMainDartFile(target);
if (!FileSystemEntity.isFileSync(mainPath)) {
String message = 'Tried to run $mainPath, but that file does not exist.';
if (target == null)
message += '\nConsider using the -t option to specify the Dart file to start.';
logging.severe(message);
return 1;
}
if (stop) {
logging.fine('Running stop command.');
stopAll(devices, applicationPackages);
}
if (install) {
logging.fine('Running install command.');
installApp(devices, applicationPackages);
}
bool startedSomething = false;
for (Device device in devices.all) {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null || !device.isConnected())
continue;
logging.fine('Running build command for $device.');
if (device.platform == TargetPlatform.android) {
await flx.buildInTempDir(
toolchain,
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: checked,
traceStartup: traceStartup,
route: route,
clearLogs: clearLogs
)) {
startedSomething = true;
}
}
);
} else {
bool result = await device.startApp(package);
if (!result) {
logging.severe('Could not start \'${package.name}\' on \'${device.id}\'');
} else {
startedSomething = true;
}
}
}
if (!startedSomething) {
if (!devices.all.any((device) => device.isConnected())) {
logging.severe('Unable to run application - no connected devices.');
} else {
logging.severe('Unable to run application.');
}
}
return startedSomething ? 0 : 2;
}
...@@ -18,17 +18,19 @@ class StopCommand extends FlutterCommand { ...@@ -18,17 +18,19 @@ class StopCommand extends FlutterCommand {
return await stop() ? 0 : 2; return await stop() ? 0 : 2;
} }
Future<bool> stop() async { Future<bool> stop() => stopAll(devices, applicationPackages);
bool stoppedSomething = false; }
for (Device device in devices.all) { Future<bool> stopAll(DeviceStore devices, ApplicationPackageStore applicationPackages) async {
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); bool stoppedSomething = false;
if (package == null || !device.isConnected())
continue; for (Device device in devices.all) {
if (await device.stopApp(package)) ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
stoppedSomething = true; if (package == null || !device.isConnected())
} continue;
if (await device.stopApp(package))
return stoppedSomething; stoppedSomething = true;
} }
return stoppedSomething;
} }
...@@ -310,7 +310,7 @@ class IOSSimulator extends Device { ...@@ -310,7 +310,7 @@ class IOSSimulator extends Device {
String output = runCheckedSync([xcrunPath, 'simctl', 'list', 'devices']); String output = runCheckedSync([xcrunPath, 'simctl', 'list', 'devices']);
Match match; Match match;
/// iPhone 6s Plus (8AC808E1-6BAE-4153-BBC5-77F83814D414) (Booted) // iPhone 6s Plus (8AC808E1-6BAE-4153-BBC5-77F83814D414) (Booted)
Iterable<Match> matches = new RegExp( Iterable<Match> matches = new RegExp(
r'[\W]*(.*) \(([^\)]+)\) \(Booted\)', r'[\W]*(.*) \(([^\)]+)\) \(Booted\)',
multiLine: true multiLine: true
......
...@@ -47,12 +47,6 @@ abstract class FlutterCommand extends Command { ...@@ -47,12 +47,6 @@ abstract class FlutterCommand extends Command {
connectToDevices(); connectToDevices();
} }
void inheritFromParent(FlutterCommand other) {
applicationPackages = other.applicationPackages;
toolchain = other.toolchain;
devices = other.devices;
}
Future<int> run() async { Future<int> run() async {
if (requiresProjectRoot && !validateProjectRoot()) if (requiresProjectRoot && !validateProjectRoot())
return 1; return 1;
......
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