Commit 18212382 authored by Dan Rubel's avatar Dan Rubel Committed by GitHub

Refactor flutter command execution (#5892)

* refactor _run to runCmd

* replace requiresProjectRoot getter with call to commandValidator

* replace requiresDevice getter with call to findTargetDevice

* trace command requires a debug connection, not a device

* inline androidOnly getter

* rename command methods to verifyTheRunCmd and runCmd

* move common verification into BuildSubCommand

* rename deviceForCommand to device

* rename methods to verifyThenRunCommand and runCommand
parent 0873f3e1
...@@ -60,10 +60,7 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -60,10 +60,7 @@ class AnalyzeCommand extends FlutterCommand {
} }
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() => argResults['watch'] ? _analyzeWatch() : _analyzeOnce();
@override
Future<int> runInProject() => argResults['watch'] ? _analyzeWatch() : _analyzeOnce();
List<String> flutterRootComponents; List<String> flutterRootComponents;
bool isFlutterLibrary(String filename) { bool isFlutterLibrary(String filename) {
......
...@@ -32,13 +32,28 @@ class BuildCommand extends FlutterCommand { ...@@ -32,13 +32,28 @@ class BuildCommand extends FlutterCommand {
final String description = 'Flutter build commands.'; final String description = 'Flutter build commands.';
@override @override
Future<int> runInProject() => new Future<int>.value(0); Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() => new Future<int>.value(0);
} }
abstract class BuildSubCommand extends FlutterCommand { abstract class BuildSubCommand extends FlutterCommand {
@override @override
@mustCallSuper @mustCallSuper
Future<int> runInProject() async { Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
@mustCallSuper
Future<int> runCommand() async {
if (isRunningOnBot) { if (isRunningOnBot) {
File dotPackages = new File('.packages'); File dotPackages = new File('.packages');
printStatus('Contents of .packages:'); printStatus('Contents of .packages:');
...@@ -66,7 +81,14 @@ class BuildCleanCommand extends FlutterCommand { ...@@ -66,7 +81,14 @@ class BuildCleanCommand extends FlutterCommand {
final String description = 'Delete the build/ directory.'; final String description = 'Delete the build/ directory.';
@override @override
Future<int> runInProject() async { Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() async {
Directory buildDir = new Directory(getBuildDirectory()); Directory buildDir = new Directory(getBuildDirectory());
printStatus("Deleting '${buildDir.path}${Platform.pathSeparator}'."); printStatus("Deleting '${buildDir.path}${Platform.pathSeparator}'.");
......
...@@ -42,8 +42,8 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -42,8 +42,8 @@ class BuildAotCommand extends BuildSubCommand {
final String description = "Build an ahead-of-time compiled snapshot of your app's Dart code."; final String description = "Build an ahead-of-time compiled snapshot of your app's Dart code.";
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
await super.runInProject(); await super.runCommand();
String targetPlatform = argResults['target-platform']; String targetPlatform = argResults['target-platform'];
TargetPlatform platform = getTargetPlatformForName(targetPlatform); TargetPlatform platform = getTargetPlatformForName(targetPlatform);
if (platform == null) { if (platform == null) {
......
...@@ -221,8 +221,8 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -221,8 +221,8 @@ class BuildApkCommand extends BuildSubCommand {
} }
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
await super.runInProject(); await super.runCommand();
TargetPlatform targetPlatform = _getTargetPlatform(argResults['target-arch']); TargetPlatform targetPlatform = _getTargetPlatform(argResults['target-arch']);
if (targetPlatform != TargetPlatform.android_arm && getBuildMode() != BuildMode.debug) { if (targetPlatform != TargetPlatform.android_arm && getBuildMode() != BuildMode.debug) {
......
...@@ -38,8 +38,8 @@ class BuildFlxCommand extends BuildSubCommand { ...@@ -38,8 +38,8 @@ class BuildFlxCommand extends BuildSubCommand {
'they are used by some Flutter Android and iOS runtimes.'; 'they are used by some Flutter Android and iOS runtimes.';
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
await super.runInProject(); await super.runCommand();
String outputPath = argResults['output-file']; String outputPath = argResults['output-file'];
return await build( return await build(
......
...@@ -38,11 +38,11 @@ class BuildIOSCommand extends BuildSubCommand { ...@@ -38,11 +38,11 @@ class BuildIOSCommand extends BuildSubCommand {
final String description = 'Build an iOS application bundle (Mac OS X host only).'; final String description = 'Build an iOS application bundle (Mac OS X host only).';
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
bool forSimulator = argResults['simulator']; bool forSimulator = argResults['simulator'];
defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release; defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release;
await super.runInProject(); await super.runCommand();
if (getCurrentHostPlatform() != HostPlatform.darwin_x64) { if (getCurrentHostPlatform() != HostPlatform.darwin_x64) {
printError('Building for iOS is only supported on the Mac.'); printError('Building for iOS is only supported on the Mac.');
return 1; return 1;
......
...@@ -20,10 +20,7 @@ class ChannelCommand extends FlutterCommand { ...@@ -20,10 +20,7 @@ class ChannelCommand extends FlutterCommand {
String get invocation => '${runner.executableName} $name [<channel-name>]'; String get invocation => '${runner.executableName} $name [<channel-name>]';
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
switch (argResults.rest.length) { switch (argResults.rest.length) {
case 0: case 0:
return await _listChannels(); return await _listChannels();
......
...@@ -41,15 +41,12 @@ class ConfigCommand extends FlutterCommand { ...@@ -41,15 +41,12 @@ class ConfigCommand extends FlutterCommand {
'Analytics reporting is currently ${flutterUsage.enabled ? 'enabled' : 'disabled'}.'; 'Analytics reporting is currently ${flutterUsage.enabled ? 'enabled' : 'disabled'}.';
} }
@override
bool get requiresProjectRoot => false;
/// Return `null` to disable tracking of the `config` command. /// Return `null` to disable tracking of the `config` command.
@override @override
String get usagePath => null; String get usagePath => null;
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if (argResults.wasParsed('analytics')) { if (argResults.wasParsed('analytics')) {
bool value = argResults['analytics']; bool value = argResults['analytics'];
flutterUsage.enabled = value; flutterUsage.enabled = value;
......
...@@ -41,14 +41,11 @@ class CreateCommand extends FlutterCommand { ...@@ -41,14 +41,11 @@ class CreateCommand extends FlutterCommand {
final String description = 'Create a new Flutter project.\n\n' final String description = 'Create a new Flutter project.\n\n'
'If run on a project that already exists, this will repair the project, recreating any files that are missing.'; 'If run on a project that already exists, this will repair the project, recreating any files that are missing.';
@override
bool get requiresProjectRoot => false;
@override @override
String get invocation => "${runner.executableName} $name <output directory>"; String get invocation => "${runner.executableName} $name <output directory>";
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if (argResults.rest.isEmpty) { if (argResults.rest.isEmpty) {
printStatus('No option specified for the output directory.'); printStatus('No option specified for the output directory.');
printStatus(usage); printStatus(usage);
......
...@@ -37,14 +37,11 @@ class DaemonCommand extends FlutterCommand { ...@@ -37,14 +37,11 @@ class DaemonCommand extends FlutterCommand {
@override @override
final String description = 'Run a persistent, JSON-RPC based server to communicate with devices.'; final String description = 'Run a persistent, JSON-RPC based server to communicate with devices.';
@override
bool get requiresProjectRoot => false;
@override @override
final bool hidden; final bool hidden;
@override @override
Future<int> runInProject() { Future<int> runCommand() {
printStatus('Starting device daemon...'); printStatus('Starting device daemon...');
AppContext appContext = new AppContext(); AppContext appContext = new AppContext();
......
...@@ -17,10 +17,7 @@ class DevicesCommand extends FlutterCommand { ...@@ -17,10 +17,7 @@ class DevicesCommand extends FlutterCommand {
final String description = 'List all connected devices.'; final String description = 'List all connected devices.';
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
if (!doctor.canListAnything) { if (!doctor.canListAnything) {
printError("Unable to locate a development device; please run 'flutter doctor' for " printError("Unable to locate a development device; please run 'flutter doctor' for "
"information about installing additional components."); "information about installing additional components.");
......
...@@ -15,10 +15,7 @@ class DoctorCommand extends FlutterCommand { ...@@ -15,10 +15,7 @@ class DoctorCommand extends FlutterCommand {
final String description = 'Show information about the installed tooling.'; final String description = 'Show information about the installed tooling.';
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
await doctor.diagnose(); await doctor.diagnose();
return 0; return 0;
} }
......
...@@ -86,7 +86,14 @@ class DriveCommand extends RunCommandBase { ...@@ -86,7 +86,14 @@ class DriveCommand extends RunCommandBase {
int get debugPort => int.parse(argResults['debug-port']); int get debugPort => int.parse(argResults['debug-port']);
@override @override
Future<int> runInProject() async { Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() async {
String testFile = _getTestFile(); String testFile = _getTestFile();
if (testFile == null) { if (testFile == null) {
return 1; return 1;
......
...@@ -21,14 +21,11 @@ class FormatCommand extends FlutterCommand { ...@@ -21,14 +21,11 @@ class FormatCommand extends FlutterCommand {
@override @override
final String description = 'Format one or more dart files.'; final String description = 'Format one or more dart files.';
@override
bool get requiresProjectRoot => false;
@override @override
String get invocation => "${runner.executableName} $name <one or more paths>"; String get invocation => "${runner.executableName} $name <one or more paths>";
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if (argResults.rest.isEmpty) { if (argResults.rest.isEmpty) {
printStatus('No files specified to be formatted.'); printStatus('No files specified to be formatted.');
printStatus(''); printStatus('');
......
...@@ -17,12 +17,20 @@ class InstallCommand extends FlutterCommand { ...@@ -17,12 +17,20 @@ class InstallCommand extends FlutterCommand {
@override @override
final String description = 'Install a Flutter app on an attached device.'; final String description = 'Install a Flutter app on an attached device.';
Device device;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
Device device = deviceForCommand;
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
Cache.releaseLockEarly(); Cache.releaseLockEarly();
......
...@@ -29,11 +29,20 @@ class ListenCommand extends RunCommandBase { ...@@ -29,11 +29,20 @@ class ListenCommand extends RunCommandBase {
/// Only run once. Used for testing. /// Only run once. Used for testing.
final bool singleRun; final bool singleRun;
Device device;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
Iterable<String> directories = () sync* { Iterable<String> directories = () sync* {
yield* argResults.rest; yield* argResults.rest;
yield '.'; yield '.';
...@@ -61,7 +70,7 @@ class ListenCommand extends RunCommandBase { ...@@ -61,7 +70,7 @@ class ListenCommand extends RunCommandBase {
printStatus('Re-running app...'); printStatus('Re-running app...');
result = await startApp( result = await startApp(
deviceForCommand, device,
target: targetFile, target: targetFile,
install: firstTime, install: firstTime,
stop: true, stop: true,
......
...@@ -25,16 +25,18 @@ class LogsCommand extends FlutterCommand { ...@@ -25,16 +25,18 @@ class LogsCommand extends FlutterCommand {
@override @override
final String description = 'Show log output for running Flutter apps.'; final String description = 'Show log output for running Flutter apps.';
@override Device device;
bool get requiresProjectRoot => false;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
Device device = deviceForCommand;
if (argResults['clear']) if (argResults['clear'])
device.clearLogs(); device.clearLogs();
......
...@@ -25,7 +25,14 @@ class PackagesCommand extends FlutterCommand { ...@@ -25,7 +25,14 @@ class PackagesCommand extends FlutterCommand {
final String description = 'Commands for managing Flutter packages.'; final String description = 'Commands for managing Flutter packages.';
@override @override
Future<int> runInProject() => new Future<int>.value(0); Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() => new Future<int>.value(0);
} }
class PackagesGetCommand extends FlutterCommand { class PackagesGetCommand extends FlutterCommand {
...@@ -41,15 +48,12 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -41,15 +48,12 @@ class PackagesGetCommand extends FlutterCommand {
String get description => String get description =>
(upgrade ? 'Upgrade' : 'Get') + ' packages in a Flutter project.'; (upgrade ? 'Upgrade' : 'Get') + ' packages in a Flutter project.';
@override
bool get requiresProjectRoot => false;
@override @override
String get invocation => String get invocation =>
"${runner.executableName} packages $name [<target directory>]"; "${runner.executableName} packages $name [<target directory>]";
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if (argResults.rest.length > 1) { if (argResults.rest.length > 1) {
printStatus('Too many arguments.'); printStatus('Too many arguments.');
printStatus(usage); printStatus(usage);
......
...@@ -20,10 +20,7 @@ class PrecacheCommand extends FlutterCommand { ...@@ -20,10 +20,7 @@ class PrecacheCommand extends FlutterCommand {
final String description = 'Populates the Flutter tool\'s cache of binary artifacts.'; final String description = 'Populates the Flutter tool\'s cache of binary artifacts.';
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
if (argResults['all-platforms']) if (argResults['all-platforms'])
cache.includeAllPlatforms = true; cache.includeAllPlatforms = true;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter_tools/src/device.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../android/android_device.dart'; import '../android/android_device.dart';
...@@ -29,14 +30,20 @@ class RefreshCommand extends FlutterCommand { ...@@ -29,14 +30,20 @@ class RefreshCommand extends FlutterCommand {
); );
} }
@override Device device;
bool get androidOnly => true;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
device = await findTargetDevice(androidOnly: true);
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools'); Directory tempDir = await Directory.systemTemp.createTemp('flutter_tools');
try { try {
String snapshotPath = path.join(tempDir.path, 'snapshot_blob.bin'); String snapshotPath = path.join(tempDir.path, 'snapshot_blob.bin');
...@@ -49,8 +56,6 @@ class RefreshCommand extends FlutterCommand { ...@@ -49,8 +56,6 @@ class RefreshCommand extends FlutterCommand {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
AndroidDevice device = deviceForCommand;
String activity = argResults['activity']; String activity = argResults['activity'];
if (activity == null) { if (activity == null) {
AndroidApk apk = applicationPackages.getPackageForPlatform(device.platform); AndroidApk apk = applicationPackages.getPackageForPlatform(device.platform);
...@@ -62,7 +67,9 @@ class RefreshCommand extends FlutterCommand { ...@@ -62,7 +67,9 @@ class RefreshCommand extends FlutterCommand {
} }
} }
bool success = await device.refreshSnapshot(activity, snapshotPath); AndroidDevice androidDevice = device;
bool success = await androidDevice.refreshSnapshot(activity, snapshotPath);
if (!success) { if (!success) {
printError('Error refreshing snapshot on $device.'); printError('Error refreshing snapshot on $device.');
return 1; return 1;
......
...@@ -83,13 +83,10 @@ class RunCommand extends RunCommandBase { ...@@ -83,13 +83,10 @@ class RunCommand extends RunCommandBase {
argParser.addFlag('benchmark', negatable: false, hide: true); argParser.addFlag('benchmark', negatable: false, hide: true);
} }
@override Device device;
bool get requiresDevice => true;
@override @override
String get usagePath { String get usagePath {
Device device = deviceForCommand;
String command = shouldUseHotMode() ? 'hotrun' : name; String command = shouldUseHotMode() ? 'hotrun' : name;
if (device == null) if (device == null)
...@@ -116,7 +113,17 @@ class RunCommand extends RunCommandBase { ...@@ -116,7 +113,17 @@ class RunCommand extends RunCommandBase {
} }
@override @override
Future<int> runInProject() async { Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() async {
int debugPort; int debugPort;
if (argResults['debug-port'] != null) { if (argResults['debug-port'] != null) {
...@@ -128,7 +135,7 @@ class RunCommand extends RunCommandBase { ...@@ -128,7 +135,7 @@ class RunCommand extends RunCommandBase {
} }
} }
if (deviceForCommand.isLocalEmulator && !isEmulatorBuildMode(getBuildMode())) { if (device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode())) {
printError('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.'); printError('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.');
return 1; return 1;
} }
...@@ -152,7 +159,7 @@ class RunCommand extends RunCommandBase { ...@@ -152,7 +159,7 @@ class RunCommand extends RunCommandBase {
final bool hotMode = shouldUseHotMode(); final bool hotMode = shouldUseHotMode();
if (hotMode) { if (hotMode) {
if (!deviceForCommand.supportsHotMode) { if (!device.supportsHotMode) {
printError('Hot mode is not supported by this device. ' printError('Hot mode is not supported by this device. '
'Run with --no-hot.'); 'Run with --no-hot.');
return 1; return 1;
...@@ -168,14 +175,14 @@ class RunCommand extends RunCommandBase { ...@@ -168,14 +175,14 @@ class RunCommand extends RunCommandBase {
if (hotMode) { if (hotMode) {
runner = new HotRunner( runner = new HotRunner(
deviceForCommand, device,
target: targetFile, target: targetFile,
debuggingOptions: options, debuggingOptions: options,
benchmarkMode: argResults['benchmark'], benchmarkMode: argResults['benchmark'],
); );
} else { } else {
runner = new RunAndStayResident( runner = new RunAndStayResident(
deviceForCommand, device,
target: targetFile, target: targetFile,
debuggingOptions: options, debuggingOptions: options,
traceStartup: traceStartup, traceStartup: traceStartup,
......
...@@ -42,9 +42,6 @@ class RunMojoCommand extends FlutterCommand { ...@@ -42,9 +42,6 @@ class RunMojoCommand extends FlutterCommand {
@override @override
final bool hidden; final bool hidden;
@override
bool get requiresProjectRoot => false;
// TODO(abarth): Why not use path.absolute? // TODO(abarth): Why not use path.absolute?
String _makePathAbsolute(String relativePath) { String _makePathAbsolute(String relativePath) {
File file = new File(relativePath); File file = new File(relativePath);
...@@ -127,7 +124,7 @@ class RunMojoCommand extends FlutterCommand { ...@@ -127,7 +124,7 @@ class RunMojoCommand extends FlutterCommand {
} }
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if ((argResults['mojo-path'] == null && argResults['devtools-path'] == null) || (argResults['mojo-path'] != null && argResults['devtools-path'] != null)) { if ((argResults['mojo-path'] == null && argResults['devtools-path'] == null) || (argResults['mojo-path'] != null && argResults['devtools-path'] != null)) {
printError('Must specify either --mojo-path or --devtools-path.'); printError('Must specify either --mojo-path or --devtools-path.');
return 1; return 1;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter_tools/src/device.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../base/utils.dart'; import '../base/utils.dart';
...@@ -27,16 +28,20 @@ class ScreenshotCommand extends FlutterCommand { ...@@ -27,16 +28,20 @@ class ScreenshotCommand extends FlutterCommand {
@override @override
final List<String> aliases = <String>['pic']; final List<String> aliases = <String>['pic'];
@override Device device;
bool get requiresProjectRoot => false;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
if (!deviceForCommand.supportsScreenshot) { if (!device.supportsScreenshot) {
printError('Screenshot not supported for ${deviceForCommand.name}.'); printError('Screenshot not supported for ${device.name}.');
return 1; return 1;
} }
...@@ -49,7 +54,7 @@ class ScreenshotCommand extends FlutterCommand { ...@@ -49,7 +54,7 @@ class ScreenshotCommand extends FlutterCommand {
} }
try { try {
bool result = await deviceForCommand.takeScreenshot(outputFile); bool result = await device.takeScreenshot(outputFile);
if (result) { if (result) {
int sizeKB = outputFile.lengthSync() ~/ 1000; int sizeKB = outputFile.lengthSync() ~/ 1000;
......
...@@ -31,10 +31,7 @@ class SetupCommand extends FlutterCommand { ...@@ -31,10 +31,7 @@ class SetupCommand extends FlutterCommand {
final bool hidden; final bool hidden;
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
printStatus('Running Flutter setup...'); printStatus('Running Flutter setup...');
// setup brew on mac // setup brew on mac
......
...@@ -27,7 +27,14 @@ class SkiaCommand extends FlutterCommand { ...@@ -27,7 +27,14 @@ class SkiaCommand extends FlutterCommand {
final String description = 'Retrieve the last frame rendered by a Flutter app as a Skia picture.'; final String description = 'Retrieve the last frame rendered by a Flutter app as a Skia picture.';
@override @override
Future<int> runInProject() async { Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override
Future<int> runCommand() async {
File outputFile; File outputFile;
Uri skiaserveUri; Uri skiaserveUri;
if (argResults['output-file'] != null) { if (argResults['output-file'] != null) {
......
...@@ -17,12 +17,20 @@ class StopCommand extends FlutterCommand { ...@@ -17,12 +17,20 @@ class StopCommand extends FlutterCommand {
@override @override
final String description = 'Stop your Flutter app on an attached device.'; final String description = 'Stop your Flutter app on an attached device.';
Device device;
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
device = await findTargetDevice();
if (device == null)
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
Device device = deviceForCommand;
ApplicationPackage app = applicationPackages.getPackageForPlatform(device.platform); ApplicationPackage app = applicationPackages.getPackageForPlatform(device.platform);
if (app == null) { if (app == null) {
String platformName = getNameForTargetPlatform(device.platform); String platformName = getNameForTargetPlatform(device.platform);
......
...@@ -44,9 +44,6 @@ class TestCommand extends FlutterCommand { ...@@ -44,9 +44,6 @@ class TestCommand extends FlutterCommand {
@override @override
String get description => 'Run Flutter unit tests for the current project.'; String get description => 'Run Flutter unit tests for the current project.';
@override
bool get requiresProjectRoot => false;
@override @override
Validator commandValidator = () { Validator commandValidator = () {
if (!FileSystemEntity.isFileSync('pubspec.yaml')) { if (!FileSystemEntity.isFileSync('pubspec.yaml')) {
...@@ -147,7 +144,7 @@ class TestCommand extends FlutterCommand { ...@@ -147,7 +144,7 @@ class TestCommand extends FlutterCommand {
} }
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
List<String> testArgs = argResults.rest.map((String testPath) => path.absolute(testPath)).toList(); List<String> testArgs = argResults.rest.map((String testPath) => path.absolute(testPath)).toList();
if (!commandValidator()) if (!commandValidator())
......
...@@ -46,10 +46,14 @@ class TraceCommand extends FlutterCommand { ...@@ -46,10 +46,14 @@ class TraceCommand extends FlutterCommand {
'with --start and later with --stop.'; 'with --start and later with --stop.';
@override @override
bool get requiresDevice => true; Future<int> verifyThenRunCommand() async {
if (!commandValidator())
return 1;
return super.verifyThenRunCommand();
}
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
int observatoryPort = int.parse(argResults['debug-port']); int observatoryPort = int.parse(argResults['debug-port']);
Tracing tracing; Tracing tracing;
......
...@@ -32,9 +32,6 @@ class UpdatePackagesCommand extends FlutterCommand { ...@@ -32,9 +32,6 @@ class UpdatePackagesCommand extends FlutterCommand {
@override @override
final bool hidden; final bool hidden;
@override
bool get requiresProjectRoot => false;
Future<Null> _downloadCoverageData() async { Future<Null> _downloadCoverageData() async {
Status status = logger.startProgress("Downloading lcov data for package:flutter..."); Status status = logger.startProgress("Downloading lcov data for package:flutter...");
final List<int> data = await fetchUrl(Uri.parse('https://storage.googleapis.com/flutter_infra/flutter/coverage/lcov.info')); final List<int> data = await fetchUrl(Uri.parse('https://storage.googleapis.com/flutter_infra/flutter/coverage/lcov.info'));
...@@ -49,7 +46,7 @@ class UpdatePackagesCommand extends FlutterCommand { ...@@ -49,7 +46,7 @@ class UpdatePackagesCommand extends FlutterCommand {
} }
@override @override
Future<int> runInProject() async { Future<int> runCommand() async {
try { try {
final Stopwatch timer = new Stopwatch()..start(); final Stopwatch timer = new Stopwatch()..start();
int count = 0; int count = 0;
......
...@@ -21,10 +21,7 @@ class UpgradeCommand extends FlutterCommand { ...@@ -21,10 +21,7 @@ class UpgradeCommand extends FlutterCommand {
final String description = 'Upgrade your copy of Flutter.'; final String description = 'Upgrade your copy of Flutter.';
@override @override
bool get requiresProjectRoot => false; Future<int> runCommand() async {
@override
Future<int> runInProject() async {
try { try {
runCheckedSync(<String>[ runCheckedSync(<String>[
'git', 'rev-parse', '@{u}' 'git', 'rev-parse', '@{u}'
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:meta/meta.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../build_info.dart'; import '../build_info.dart';
...@@ -27,15 +28,6 @@ abstract class FlutterCommand extends Command { ...@@ -27,15 +28,6 @@ abstract class FlutterCommand extends Command {
@override @override
FlutterCommandRunner get runner => super.runner; FlutterCommandRunner get runner => super.runner;
/// Whether this command needs to be run from the root of a project.
bool get requiresProjectRoot => true;
/// Whether this command requires a (single) Flutter target device to be connected.
bool get requiresDevice => false;
/// Whether this command only applies to Android devices.
bool get androidOnly => false;
/// Whether this command uses the 'target' option. /// Whether this command uses the 'target' option.
bool _usesTargetOption = false; bool _usesTargetOption = false;
...@@ -100,7 +92,7 @@ abstract class FlutterCommand extends Command { ...@@ -100,7 +92,7 @@ abstract class FlutterCommand extends Command {
return _defaultBuildMode; return _defaultBuildMode;
} }
void _setupApplicationPackages() { void setupApplicationPackages() {
applicationPackages ??= new ApplicationPackageStore(); applicationPackages ??= new ApplicationPackageStore();
} }
...@@ -108,12 +100,21 @@ abstract class FlutterCommand extends Command { ...@@ -108,12 +100,21 @@ abstract class FlutterCommand extends Command {
/// tracking of the command. /// tracking of the command.
String get usagePath => name; String get usagePath => name;
/// Runs this command.
///
/// Rather than overriding this method, subclasses should override
/// [verifyThenRunCommand] to perform any verification
/// and [runCommand] to execute the command
/// so that this method can record and report the overall time to analytics.
@override @override
Future<int> run() { Future<int> run() {
Stopwatch stopwatch = new Stopwatch()..start(); Stopwatch stopwatch = new Stopwatch()..start();
UsageTimer analyticsTimer = usagePath == null ? null : flutterUsage.startTimer(name); UsageTimer analyticsTimer = usagePath == null ? null : flutterUsage.startTimer(name);
return _run().then((int exitCode) { if (flutterUsage.isFirstRun)
flutterUsage.printUsage();
return verifyThenRunCommand().then((int exitCode) {
int ms = stopwatch.elapsedMilliseconds; int ms = stopwatch.elapsedMilliseconds;
printTrace("'flutter $name' took ${ms}ms; exiting with code $exitCode."); printTrace("'flutter $name' took ${ms}ms; exiting with code $exitCode.");
analyticsTimer?.finish(); analyticsTimer?.finish();
...@@ -121,55 +122,15 @@ abstract class FlutterCommand extends Command { ...@@ -121,55 +122,15 @@ abstract class FlutterCommand extends Command {
}); });
} }
Future<int> _run() async { /// Perform validation then call [runCommand] to execute the command.
if (requiresProjectRoot && !commandValidator()) /// Return a [Future] that completes with an exit code
return 1; /// indicating whether execution was successful.
///
// Ensure at least one toolchain is installed. /// Subclasses should override this method to perform verification
if (requiresDevice && !doctor.canLaunchAnything) { /// then call this method to execute the command
printError("Unable to locate a development device; please run 'flutter doctor' " /// rather than calling [runCommand] directly.
"for information about installing additional components."); @mustCallSuper
return 1; Future<int> verifyThenRunCommand() async {
}
// Validate devices.
if (requiresDevice) {
List<Device> devices = await deviceManager.getDevices();
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
printStatus("No devices found with name or id "
"matching '${deviceManager.specifiedDeviceId}'");
return 1;
} else if (devices.isEmpty) {
printNoConnectedDevices();
return 1;
}
devices = devices.where((Device device) => device.isSupported()).toList();
if (androidOnly)
devices = devices.where((Device device) => device.platform == TargetPlatform.android_arm).toList();
if (devices.isEmpty) {
printStatus('No supported devices connected.');
return 1;
} else if (devices.length > 1) {
if (deviceManager.hasSpecifiedDeviceId) {
printStatus("Found ${devices.length} devices with name or id matching "
"'${deviceManager.specifiedDeviceId}':");
} else {
printStatus("More than one device connected; please specify a device with "
"the '-d <deviceId>' flag.");
devices = await deviceManager.getAllConnectedDevices();
}
printStatus('');
Device.printDevices(devices);
return 1;
} else {
_deviceForCommand = devices.single;
}
}
// Populate the cache. We call this before pub get below so that the sky_engine // Populate the cache. We call this before pub get below so that the sky_engine
// package is available in the flutter cache for pub to find. // package is available in the flutter cache for pub to find.
await cache.updateAll(); await cache.updateAll();
...@@ -180,16 +141,62 @@ abstract class FlutterCommand extends Command { ...@@ -180,16 +141,62 @@ abstract class FlutterCommand extends Command {
return exitCode; return exitCode;
} }
if (flutterUsage.isFirstRun) setupApplicationPackages();
flutterUsage.printUsage();
_setupApplicationPackages();
String commandPath = usagePath; String commandPath = usagePath;
if (commandPath != null) if (commandPath != null)
flutterUsage.sendCommand(usagePath); flutterUsage.sendCommand(usagePath);
return await runInProject(); return await runCommand();
}
/// Subclasses must implement this to execute the command.
Future<int> runCommand();
/// Find and return the target [Device] based upon currently connected
/// devices and criteria entered by the user on the command line.
/// If a device cannot be found that meets specified criteria,
/// then print an error message and return `null`.
Future<Device> findTargetDevice({bool androidOnly: false}) async {
if (!doctor.canLaunchAnything) {
printError("Unable to locate a development device; please run 'flutter doctor' "
"for information about installing additional components.");
return null;
}
List<Device> devices = await deviceManager.getDevices();
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
printStatus("No devices found with name or id "
"matching '${deviceManager.specifiedDeviceId}'");
return null;
} else if (devices.isEmpty) {
printNoConnectedDevices();
return null;
}
devices = devices.where((Device device) => device.isSupported()).toList();
if (androidOnly)
devices = devices.where((Device device) => device.platform == TargetPlatform.android_arm).toList();
if (devices.isEmpty) {
printStatus('No supported devices connected.');
return null;
} else if (devices.length > 1) {
if (deviceManager.hasSpecifiedDeviceId) {
printStatus("Found ${devices.length} devices with name or id matching "
"'${deviceManager.specifiedDeviceId}':");
} else {
printStatus("More than one device connected; please specify a device with "
"the '-d <deviceId>' flag.");
devices = await deviceManager.getAllConnectedDevices();
}
printStatus('');
Device.printDevices(devices);
return null;
}
return devices.single;
} }
void printNoConnectedDevices() { void printNoConnectedDevices() {
...@@ -230,12 +237,5 @@ abstract class FlutterCommand extends Command { ...@@ -230,12 +237,5 @@ abstract class FlutterCommand extends Command {
return true; return true;
} }
Future<int> runInProject();
// This is calculated in run() if the command has [requiresDevice] specified.
Device _deviceForCommand;
Device get deviceForCommand => _deviceForCommand;
ApplicationPackageStore applicationPackages; ApplicationPackageStore applicationPackages;
} }
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