Commit 0770c3c1 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tools] Adds some support for '-d all' (#9585)

parent 9558ac7d
...@@ -353,11 +353,13 @@ class AppDomain extends Domain { ...@@ -353,11 +353,13 @@ class AppDomain extends Domain {
final Directory cwd = fs.currentDirectory; final Directory cwd = fs.currentDirectory;
fs.currentDirectory = fs.directory(projectDirectory); fs.currentDirectory = fs.directory(projectDirectory);
final FlutterDevice flutterDevice = new FlutterDevice(device);
ResidentRunner runner; ResidentRunner runner;
if (enableHotReload) { if (enableHotReload) {
runner = new HotRunner( runner = new HotRunner(
device, <FlutterDevice>[flutterDevice],
target: target, target: target,
debuggingOptions: options, debuggingOptions: options,
usesTerminalUI: false, usesTerminalUI: false,
...@@ -368,7 +370,7 @@ class AppDomain extends Domain { ...@@ -368,7 +370,7 @@ class AppDomain extends Domain {
); );
} else { } else {
runner = new ColdRunner( runner = new ColdRunner(
device, <FlutterDevice>[flutterDevice],
target: target, target: target,
debuggingOptions: options, debuggingOptions: options,
usesTerminalUI: false, usesTerminalUI: false,
...@@ -448,7 +450,7 @@ class AppDomain extends Domain { ...@@ -448,7 +450,7 @@ class AppDomain extends Domain {
if (app == null) if (app == null)
throw "app '$appId' not found"; throw "app '$appId' not found";
final Isolate isolate = app.runner.currentView.uiIsolate; final Isolate isolate = app.runner.flutterDevices.first.views.first.uiIsolate;
final Map<String, dynamic> result = await isolate.invokeFlutterExtensionRpcRaw(methodName, params: params); final Map<String, dynamic> result = await isolate.invokeFlutterExtensionRpcRaw(methodName, params: params);
if (result == null) if (result == null)
return new OperationResult(1, 'method not available: $methodName'); return new OperationResult(1, 'method not available: $methodName');
......
...@@ -16,6 +16,7 @@ import '../device.dart'; ...@@ -16,6 +16,7 @@ import '../device.dart';
import '../flx.dart' as flx; import '../flx.dart' as flx;
import '../fuchsia/fuchsia_device.dart'; import '../fuchsia/fuchsia_device.dart';
import '../globals.dart'; import '../globals.dart';
import '../resident_runner.dart';
import '../run_hot.dart'; import '../run_hot.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../vmservice.dart'; import '../vmservice.dart';
...@@ -112,18 +113,21 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -112,18 +113,21 @@ class FuchsiaReloadCommand extends FlutterCommand {
final List<String> fullAddresses = targetPorts.map( final List<String> fullAddresses = targetPorts.map(
(int p) => '$_address:$p' (int p) => '$_address:$p'
).toList(); ).toList();
final List<Uri> observatoryUris = fullAddresses.map(
(String a) => Uri.parse('http://$a')
).toList();
final FuchsiaDevice device = new FuchsiaDevice(fullAddresses[0]); final FuchsiaDevice device = new FuchsiaDevice(fullAddresses[0]);
final FlutterDevice flutterDevice = new FlutterDevice(device);
flutterDevice.observatoryUris = observatoryUris;
final HotRunner hotRunner = new HotRunner( final HotRunner hotRunner = new HotRunner(
device, <FlutterDevice>[flutterDevice],
debuggingOptions: new DebuggingOptions.enabled(getBuildMode()), debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
target: _target, target: _target,
projectRootPath: _fuchsiaProjectPath, projectRootPath: _fuchsiaProjectPath,
packagesFilePath: _dotPackagesPath packagesFilePath: _dotPackagesPath
); );
final List<Uri> observatoryUris =
fullAddresses.map((String a) => Uri.parse('http://$a')).toList();
printStatus('Connecting to $_binaryName'); printStatus('Connecting to $_binaryName');
await hotRunner.attach(observatoryUris, isolateFilter: isolateName); await hotRunner.attach(viewFilter: isolateName);
} }
// A cache of VMService connections. // A cache of VMService connections.
...@@ -151,12 +155,12 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -151,12 +155,12 @@ class FuchsiaReloadCommand extends FlutterCommand {
} }
// Find ports where there is a view isolate with the given name // Find ports where there is a view isolate with the given name
Future<List<int>> _filterPorts(List<int> ports, String isolateFilter) async { Future<List<int>> _filterPorts(List<int> ports, String viewFilter) async {
final List<int> result = <int>[]; final List<int> result = <int>[];
for (FlutterView v in await _getViews(ports)) { for (FlutterView v in await _getViews(ports)) {
final Uri addr = v.owner.vmService.httpAddress; final Uri addr = v.owner.vmService.httpAddress;
printTrace('At $addr, found view: ${v.uiIsolate.name}'); printTrace('At $addr, found view: ${v.uiIsolate.name}');
if (v.uiIsolate.name.indexOf(isolateFilter) == 0) if (v.uiIsolate.name.indexOf(viewFilter) == 0)
result.add(addr.port); result.add(addr.port);
} }
return result; return result;
......
...@@ -150,17 +150,20 @@ class RunCommand extends RunCommandBase { ...@@ -150,17 +150,20 @@ class RunCommand extends RunCommandBase {
}; };
} }
Device device; List<Device> devices;
@override @override
Future<String> get usagePath async { Future<String> get usagePath async {
final String command = shouldUseHotMode() ? 'hotrun' : name; final String command = shouldUseHotMode() ? 'hotrun' : name;
if (device == null) if (devices == null)
return command; return command;
// Return 'run/ios'. // Return 'run/ios'.
return '$command/${getNameForTargetPlatform(await device.targetPlatform)}'; if (devices.length > 1)
return '$command/all';
else
return '$command/${getNameForTargetPlatform(await devices[0].targetPlatform)}';
} }
@override @override
...@@ -199,9 +202,11 @@ class RunCommand extends RunCommandBase { ...@@ -199,9 +202,11 @@ class RunCommand extends RunCommandBase {
@override @override
Future<Null> verifyThenRunCommand() async { Future<Null> verifyThenRunCommand() async {
commandValidator(); commandValidator();
device = await findTargetDevice(); devices = await findAllTargetDevices();
if (device == null) if (devices == null)
throwToolExit(null); throwToolExit(null);
if (deviceManager.hasSpecifiedAllDevices && runningWithPrebuiltApplication)
throwToolExit('Using -d all with --use-application-binary is not supported');
return super.verifyThenRunCommand(); return super.verifyThenRunCommand();
} }
...@@ -221,7 +226,6 @@ class RunCommand extends RunCommandBase { ...@@ -221,7 +226,6 @@ class RunCommand extends RunCommandBase {
@override @override
Future<Null> runCommand() async { Future<Null> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
// Enable hot mode by default if `--no-hot` was not passed and we are in // Enable hot mode by default if `--no-hot` was not passed and we are in
...@@ -229,17 +233,20 @@ class RunCommand extends RunCommandBase { ...@@ -229,17 +233,20 @@ class RunCommand extends RunCommandBase {
final bool hotMode = shouldUseHotMode(); final bool hotMode = shouldUseHotMode();
if (argResults['machine']) { if (argResults['machine']) {
if (devices.length > 1)
throwToolExit('--machine does not support -d all.');
final Daemon daemon = new Daemon(stdinCommandStream, stdoutCommandResponse, final Daemon daemon = new Daemon(stdinCommandStream, stdoutCommandResponse,
notifyingLogger: new NotifyingLogger(), logToStdout: true); notifyingLogger: new NotifyingLogger(), logToStdout: true);
AppInstance app; AppInstance app;
try { try {
app = await daemon.appDomain.startApp( app = await daemon.appDomain.startApp(
device, fs.currentDirectory.path, targetFile, route, devices.first, fs.currentDirectory.path, targetFile, route,
_createDebuggingOptions(), hotMode, _createDebuggingOptions(), hotMode,
applicationBinary: argResults['use-application-binary'], applicationBinary: argResults['use-application-binary'],
projectRootPath: argResults['project-root'], projectRootPath: argResults['project-root'],
packagesFilePath: argResults['packages'], packagesFilePath: argResults['packages'],
projectAssets: argResults['project-assets']); projectAssets: argResults['project-assets']
);
} catch (error) { } catch (error) {
throwToolExit(error.toString()); throwToolExit(error.toString());
} }
...@@ -249,12 +256,16 @@ class RunCommand extends RunCommandBase { ...@@ -249,12 +256,16 @@ class RunCommand extends RunCommandBase {
return null; return null;
} }
for (Device device in devices) {
if (await device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode())) if (await device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode()))
throwToolExit('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.'); throwToolExit('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.');
}
if (hotMode) { if (hotMode) {
for (Device device in devices) {
if (!device.supportsHotMode) if (!device.supportsHotMode)
throwToolExit('Hot mode is not supported by this device. Run with --no-hot.'); throwToolExit('Hot mode is not supported by ${device.name}. Run with --no-hot.');
}
} }
final String pidFile = argResults['pid-file']; final String pidFile = argResults['pid-file'];
...@@ -262,11 +273,15 @@ class RunCommand extends RunCommandBase { ...@@ -262,11 +273,15 @@ class RunCommand extends RunCommandBase {
// Write our pid to the file. // Write our pid to the file.
fs.file(pidFile).writeAsStringSync(pid.toString()); fs.file(pidFile).writeAsStringSync(pid.toString());
} }
ResidentRunner runner;
final List<FlutterDevice> flutterDevices = devices.map((Device device) {
return new FlutterDevice(device);
}).toList();
ResidentRunner runner;
if (hotMode) { if (hotMode) {
runner = new HotRunner( runner = new HotRunner(
device, flutterDevices,
target: targetFile, target: targetFile,
debuggingOptions: _createDebuggingOptions(), debuggingOptions: _createDebuggingOptions(),
benchmarkMode: argResults['benchmark'], benchmarkMode: argResults['benchmark'],
...@@ -279,7 +294,7 @@ class RunCommand extends RunCommandBase { ...@@ -279,7 +294,7 @@ class RunCommand extends RunCommandBase {
); );
} else { } else {
runner = new ColdRunner( runner = new ColdRunner(
device, flutterDevices,
target: targetFile, target: targetFile,
debuggingOptions: _createDebuggingOptions(), debuggingOptions: _createDebuggingOptions(),
traceStartup: traceStartup, traceStartup: traceStartup,
......
...@@ -32,11 +32,26 @@ class DeviceManager { ...@@ -32,11 +32,26 @@ class DeviceManager {
final List<DeviceDiscovery> _deviceDiscoverers = <DeviceDiscovery>[]; final List<DeviceDiscovery> _deviceDiscoverers = <DeviceDiscovery>[];
String _specifiedDeviceId;
/// A user-specified device ID. /// A user-specified device ID.
String specifiedDeviceId; String get specifiedDeviceId {
if (_specifiedDeviceId == null || _specifiedDeviceId == 'all')
return null;
return _specifiedDeviceId;
}
set specifiedDeviceId(String id) {
_specifiedDeviceId = id;
}
/// True when the user has specified a single specific device.
bool get hasSpecifiedDeviceId => specifiedDeviceId != null; bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
/// True when the user has specified all devices by setting
/// specifiedDeviceId = 'all'.
bool get hasSpecifiedAllDevices => _specifiedDeviceId == 'all';
Stream<Device> getDevicesById(String deviceId) async* { Stream<Device> getDevicesById(String deviceId) async* {
final Stream<Device> devices = getAllConnectedDevices(); final Stream<Device> devices = getAllConnectedDevices();
deviceId = deviceId.toLowerCase(); deviceId = deviceId.toLowerCase();
......
...@@ -6,10 +6,7 @@ import 'dart:async'; ...@@ -6,10 +6,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'application_package.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/utils.dart';
import 'build_info.dart';
import 'commands/trace.dart'; import 'commands/trace.dart';
import 'device.dart'; import 'device.dart';
import 'globals.dart'; import 'globals.dart';
...@@ -17,25 +14,22 @@ import 'resident_runner.dart'; ...@@ -17,25 +14,22 @@ import 'resident_runner.dart';
class ColdRunner extends ResidentRunner { class ColdRunner extends ResidentRunner {
ColdRunner( ColdRunner(
Device device, { List<FlutterDevice> devices, {
String target, String target,
DebuggingOptions debuggingOptions, DebuggingOptions debuggingOptions,
bool usesTerminalUI: true, bool usesTerminalUI: true,
this.traceStartup: false, this.traceStartup: false,
this.applicationBinary, this.applicationBinary,
bool stayResident: true, bool stayResident: true,
}) : super(device, }) : super(devices,
target: target, target: target,
debuggingOptions: debuggingOptions, debuggingOptions: debuggingOptions,
usesTerminalUI: usesTerminalUI, usesTerminalUI: usesTerminalUI,
stayResident: stayResident); stayResident: stayResident);
LaunchResult _result;
final bool traceStartup; final bool traceStartup;
final String applicationBinary; final String applicationBinary;
bool get prebuiltMode => applicationBinary != null;
@override @override
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
...@@ -43,6 +37,7 @@ class ColdRunner extends ResidentRunner { ...@@ -43,6 +37,7 @@ class ColdRunner extends ResidentRunner {
String route, String route,
bool shouldBuild: true bool shouldBuild: true
}) async { }) async {
final bool prebuiltMode = applicationBinary != null;
if (!prebuiltMode) { if (!prebuiltMode) {
if (!fs.isFileSync(mainPath)) { if (!fs.isFileSync(mainPath)) {
String message = 'Tried to run $mainPath, but that file does not exist.'; String message = 'Tried to run $mainPath, but that file does not exist.';
...@@ -53,80 +48,50 @@ class ColdRunner extends ResidentRunner { ...@@ -53,80 +48,50 @@ class ColdRunner extends ResidentRunner {
} }
} }
final String modeName = getModeName(debuggingOptions.buildMode); for (FlutterDevice device in flutterDevices) {
if (mainPath == null) { final int result = await device.runCold(
assert(prebuiltMode); coldRunner: this,
printStatus('Launching ${package.displayName} on ${device.name} in $modeName mode...');
} else {
printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
}
final TargetPlatform targetPlatform = await device.targetPlatform;
package = getApplicationPackageForPlatform(targetPlatform, applicationBinary: applicationBinary);
if (package == null) {
String message = 'No application found for $targetPlatform.';
final String hint = getMissingPackageHintForPlatform(targetPlatform);
if (hint != null)
message += '\n$hint';
printError(message);
return 1;
}
final Stopwatch startTime = new Stopwatch()..start();
Map<String, dynamic> platformArgs;
if (traceStartup != null)
platformArgs = <String, dynamic>{ 'trace-startup': traceStartup };
await startEchoingDeviceLog(package);
_result = await device.startApp(
package,
debuggingOptions.buildMode,
mainPath: mainPath,
debuggingOptions: debuggingOptions,
platformArgs: platformArgs,
route: route, route: route,
prebuiltApplication: prebuiltMode, shouldBuild: shouldBuild,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies()
); );
if (result != 0)
if (!_result.started) { return result;
printError('Error running application on ${device.name}.');
await stopEchoingDeviceLog();
return 2;
} }
startTime.stop();
// Connect to observatory. // Connect to observatory.
if (debuggingOptions.debuggingEnabled) if (debuggingOptions.debuggingEnabled)
await connectToServiceProtocol(<Uri>[_result.observatoryUri]); await connectToServiceProtocol();
if (_result.hasObservatory) { if (flutterDevices.first.observatoryUris != null) {
// For now, only support one debugger connection.
connectionInfoCompleter?.complete(new DebugConnectionInfo( connectionInfoCompleter?.complete(new DebugConnectionInfo(
httpUri: _result.observatoryUri, httpUri: flutterDevices.first.observatoryUris.first,
wsUri: vmServices[0].wsAddress, wsUri: flutterDevices.first.vmServices.first.wsAddress,
)); ));
} }
printTrace('Application running.'); printTrace('Application running.');
if (vmServices != null && vmServices.isNotEmpty) { for (FlutterDevice device in flutterDevices) {
device.getLogReader(app: package).appPid = vmServices[0].vm.pid; if (device.vmServices == null)
await refreshViews(); continue;
printTrace('Connected to $currentView.'); device.initLogReader();
await device.refreshViews();
printTrace('Connected to ${device.device.name}');
} }
if (vmServices != null && vmServices.isNotEmpty && traceStartup) { if (traceStartup) {
printStatus('Downloading startup trace info...'); // Only trace startup for the first device.
final FlutterDevice device = flutterDevices.first;
if (device.vmServices != null && device.vmServices.isNotEmpty) {
printStatus('Downloading startup trace info for ${device.device.name}');
try { try {
await downloadStartupTrace(vmServices[0]); await downloadStartupTrace(device.vmServices.first);
} catch(error) { } catch (error) {
printError(error); printError(error);
return 2; return 2;
} }
}
appFinished(); appFinished();
} else if (stayResident) { } else if (stayResident) {
setupTerminal(); setupTerminal();
...@@ -158,8 +123,13 @@ class ColdRunner extends ResidentRunner { ...@@ -158,8 +123,13 @@ class ColdRunner extends ResidentRunner {
@override @override
void printHelp({ @required bool details }) { void printHelp({ @required bool details }) {
bool haveDetails = false; bool haveDetails = false;
if (_result.hasObservatory) for (FlutterDevice device in flutterDevices) {
printStatus('The Observatory debugger and profiler is available at: ${_result.observatoryUri}'); final String dname = device.device.name;
if (device.observatoryUris != null) {
for (Uri uri in device.observatoryUris)
printStatus('An Observatory debugger and profiler on $dname is available at $uri');
}
}
if (supportsServiceProtocol) { if (supportsServiceProtocol) {
haveDetails = true; haveDetails = true;
if (details) if (details)
...@@ -174,8 +144,10 @@ class ColdRunner extends ResidentRunner { ...@@ -174,8 +144,10 @@ class ColdRunner extends ResidentRunner {
@override @override
Future<Null> preStop() async { Future<Null> preStop() async {
for (FlutterDevice device in flutterDevices) {
// If we're running in release mode, stop the app using the device logic. // If we're running in release mode, stop the app using the device logic.
if (vmServices == null || vmServices.isEmpty) if (device.vmServices == null || device.vmServices.isEmpty)
await device.stopApp(package); await device.device.stopApp(device.package);
}
} }
} }
This diff is collapsed.
...@@ -154,11 +154,11 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -154,11 +154,11 @@ abstract class FlutterCommand extends Command<Null> {
/// Subclasses must implement this to execute the command. /// Subclasses must implement this to execute the command.
Future<Null> runCommand(); Future<Null> runCommand();
/// Find and return the target [Device] based upon currently connected /// Find and return all target [Device]s based upon currently connected
/// devices and criteria entered by the user on the command line. /// devices and criteria entered by the user on the command line.
/// If a device cannot be found that meets specified criteria, /// If no device can be found that meets specified criteria,
/// then print an error message and return `null`. /// then print an error message and return `null`.
Future<Device> findTargetDevice() async { Future<List<Device>> findAllTargetDevices() async {
if (!doctor.canLaunchAnything) { if (!doctor.canLaunchAnything) {
printError("Unable to locate a development device; please run 'flutter doctor' " printError("Unable to locate a development device; please run 'flutter doctor' "
"for information about installing additional components."); "for information about installing additional components.");
...@@ -171,6 +171,9 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -171,6 +171,9 @@ abstract class FlutterCommand extends Command<Null> {
printStatus("No devices found with name or id " printStatus("No devices found with name or id "
"matching '${deviceManager.specifiedDeviceId}'"); "matching '${deviceManager.specifiedDeviceId}'");
return null; return null;
} else if (devices.isEmpty && deviceManager.hasSpecifiedAllDevices) {
printStatus("No devices found");
return null;
} else if (devices.isEmpty) { } else if (devices.isEmpty) {
printNoConnectedDevices(); printNoConnectedDevices();
return null; return null;
...@@ -181,20 +184,39 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -181,20 +184,39 @@ abstract class FlutterCommand extends Command<Null> {
if (devices.isEmpty) { if (devices.isEmpty) {
printStatus('No supported devices connected.'); printStatus('No supported devices connected.');
return null; return null;
} else if (devices.length > 1) { } else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) {
if (deviceManager.hasSpecifiedDeviceId) { if (deviceManager.hasSpecifiedDeviceId) {
printStatus("Found ${devices.length} devices with name or id matching " printStatus("Found ${devices.length} devices with name or id matching "
"'${deviceManager.specifiedDeviceId}':"); "'${deviceManager.specifiedDeviceId}':");
} else { } else {
printStatus("More than one device connected; please specify a device with " printStatus("More than one device connected; please specify a device with "
"the '-d <deviceId>' flag."); "the '-d <deviceId>' flag, or use '-d all' to act on all devices.");
devices = await deviceManager.getAllConnectedDevices().toList(); devices = await deviceManager.getAllConnectedDevices().toList();
} }
printStatus(''); printStatus('');
await Device.printDevices(devices); await Device.printDevices(devices);
return null; return null;
} }
return devices.single; return devices;
}
/// 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() async {
List<Device> deviceList = await findAllTargetDevices();
if (deviceList == null)
return null;
if (deviceList.length > 1) {
printStatus("More than one device connected; please specify a device with "
"the '-d <deviceId>' flag.");
deviceList = await deviceManager.getAllConnectedDevices().toList();
printStatus('');
await Device.printDevices(deviceList);
return null;
}
return deviceList.single;
} }
void printNoConnectedDevices() { void printNoConnectedDevices() {
......
...@@ -127,12 +127,28 @@ class MockPortScanner extends PortScanner { ...@@ -127,12 +127,28 @@ class MockPortScanner extends PortScanner {
class MockDeviceManager implements DeviceManager { class MockDeviceManager implements DeviceManager {
List<Device> devices = <Device>[]; List<Device> devices = <Device>[];
String _specifiedDeviceId;
@override
String get specifiedDeviceId {
if (_specifiedDeviceId == null || _specifiedDeviceId == 'all')
return null;
return _specifiedDeviceId;
}
@override @override
String specifiedDeviceId; set specifiedDeviceId(String id) {
_specifiedDeviceId = id;
}
@override @override
bool get hasSpecifiedDeviceId => specifiedDeviceId != null; bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
@override
bool get hasSpecifiedAllDevices {
return _specifiedDeviceId != null && _specifiedDeviceId == 'all';
}
@override @override
Stream<Device> getAllConnectedDevices() => new Stream<Device>.fromIterable(devices); Stream<Device> getAllConnectedDevices() => new Stream<Device>.fromIterable(devices);
......
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