Unverified Commit 8acac060 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Remove the timeout when launching DevTools (#74859)

parent 43d604e1
...@@ -103,7 +103,7 @@ class AttachCommand extends FlutterCommand { ...@@ -103,7 +103,7 @@ class AttachCommand extends FlutterCommand {
); );
usesTrackWidgetCreation(verboseHelp: verboseHelp); usesTrackWidgetCreation(verboseHelp: verboseHelp);
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions(); addDevToolsOptions(verboseHelp: verboseHelp);
usesDeviceTimeoutOption(); usesDeviceTimeoutOption();
hotRunnerFactory ??= HotRunnerFactory(); hotRunnerFactory ??= HotRunnerFactory();
} }
...@@ -207,7 +207,8 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -207,7 +207,8 @@ known, it can be explicitly provided to attach via the command-line, e.g.
body: () => _attachToDevice(device), body: () => _attachToDevice(device),
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Artifacts: () => overrideArtifacts, Artifacts: () => overrideArtifacts,
}); },
);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
...@@ -327,6 +328,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -327,6 +328,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
allowExistingDdsInstance: true, allowExistingDdsInstance: true,
enableDevTools: boolArg(FlutterCommand.kEnableDevTools),
); );
}, },
device, device,
...@@ -365,6 +367,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -365,6 +367,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
result = await runner.attach( result = await runner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: onAppStart,
allowExistingDdsInstance: true, allowExistingDdsInstance: true,
enableDevTools: boolArg(FlutterCommand.kEnableDevTools),
); );
if (result != 0) { if (result != 0) {
throwToolExit(null, exitCode: result); throwToolExit(null, exitCode: result);
......
...@@ -515,6 +515,7 @@ class AppDomain extends Domain { ...@@ -515,6 +515,7 @@ class AppDomain extends Domain {
return runner.run( return runner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: true,
route: route, route: route,
); );
}, },
......
...@@ -136,7 +136,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment ...@@ -136,7 +136,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDeviceUserOption(); usesDeviceUserOption();
usesDeviceTimeoutOption(); usesDeviceTimeoutOption();
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions(); addDevToolsOptions(verboseHelp: verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp); addAndroidSpecificBuildOptions(hide: !verboseHelp);
} }
...@@ -621,6 +621,7 @@ class RunCommand extends RunCommandBase { ...@@ -621,6 +621,7 @@ class RunCommand extends RunCommandBase {
final int result = await runner.run( final int result = await runner.run(
appStartedCompleter: appStartedTimeRecorder, appStartedCompleter: appStartedTimeRecorder,
enableDevTools: stayResident && boolArg(FlutterCommand.kEnableDevTools),
route: route, route: route,
); );
if (result != 0) { if (result != 0) {
......
...@@ -48,8 +48,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -48,8 +48,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
@override @override
Future<void> launch(Uri vmServiceUri) async { Future<void> launch(Uri vmServiceUri) async {
// Place this entire method in a try/catch that swallows exceptions because // Place this entire method in a try/catch that swallows exceptions because
// we do not want to block Flutter run/attach operations on a DevTools // this method is guaranteed not to return a Future that throws.
// failure.
try { try {
bool offline = false; bool offline = false;
try { try {
...@@ -109,8 +108,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -109,8 +108,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
.transform(utf8.decoder) .transform(utf8.decoder)
.transform(const LineSplitter()) .transform(const LineSplitter())
.listen(_logger.printError); .listen(_logger.printError);
devToolsUri = await completer.future devToolsUrl = await completer.future;
.timeout(const Duration(seconds: 10));
} on Exception catch (e, st) { } on Exception catch (e, st) {
_logger.printError('Failed to launch DevTools: $e', stackTrace: st); _logger.printError('Failed to launch DevTools: $e', stackTrace: st);
} }
...@@ -124,7 +122,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -124,7 +122,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
'global', 'global',
'list', 'list',
]); ]);
if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) { if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) {
return true; return true;
} }
...@@ -144,7 +141,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -144,7 +141,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
if (!shouldActivate) { if (!shouldActivate) {
return false; return false;
} }
final Status status = _logger.startProgress( final Status status = _logger.startProgress(
'Activating Dart DevTools...', 'Activating Dart DevTools...',
); );
...@@ -182,7 +178,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -182,7 +178,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
@override @override
Future<void> close() async { Future<void> close() async {
devToolsUri = null; devToolsUrl = null;
if (_devToolsProcess != null) { if (_devToolsProcess != null) {
_devToolsProcess.kill(); _devToolsProcess.kill();
await _devToolsProcess.exitCode; await _devToolsProcess.exitCode;
......
...@@ -76,6 +76,7 @@ class WebDriverService extends DriverService { ...@@ -76,6 +76,7 @@ class WebDriverService extends DriverService {
final Completer<void> appStartedCompleter = Completer<void>.sync(); final Completer<void> appStartedCompleter = Completer<void>.sync();
final int result = await _residentRunner.run( final int result = await _residentRunner.run(
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: false,
route: route, route: route,
); );
_webUri = _residentRunner.uri; _webUri = _residentRunner.uri;
......
...@@ -454,6 +454,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -454,6 +454,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false, // ignored, we don't yet support devtools for web
String route, String route,
}) async { }) async {
firstBuildTime = DateTime.now(); firstBuildTime = DateTime.now();
...@@ -531,6 +532,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -531,6 +532,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
return attach( return attach(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: enableDevTools,
); );
}); });
} on WebSocketException { } on WebSocketException {
...@@ -745,6 +747,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -745,6 +747,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false, // ignored, we don't yet support devtools for web
}) async { }) async {
if (_chromiumLauncher != null) { if (_chromiumLauncher != null) {
final Chromium chrome = await _chromiumLauncher.connectedInstance; final Chromium chrome = await _chromiumLauncher.connectedInstance;
......
...@@ -795,6 +795,8 @@ abstract class ResidentRunner { ...@@ -795,6 +795,8 @@ abstract class ResidentRunner {
final CommandHelp commandHelp; final CommandHelp commandHelp;
final bool machine; final bool machine;
@visibleForTesting
DevtoolsLauncher get devToolsLauncher => _devToolsLauncher;
DevtoolsLauncher _devToolsLauncher; DevtoolsLauncher _devToolsLauncher;
bool _exited = false; bool _exited = false;
...@@ -878,6 +880,7 @@ abstract class ResidentRunner { ...@@ -878,6 +880,7 @@ abstract class ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}); });
...@@ -885,6 +888,7 @@ abstract class ResidentRunner { ...@@ -885,6 +888,7 @@ abstract class ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}); });
bool get supportsRestart => false; bool get supportsRestart => false;
...@@ -1275,19 +1279,26 @@ abstract class ResidentRunner { ...@@ -1275,19 +1279,26 @@ abstract class ResidentRunner {
return _devToolsLauncher.activeDevToolsServer; return _devToolsLauncher.activeDevToolsServer;
} }
Future<void> serveDevToolsGracefully({ // This must be guaranteed not to return a Future that fails.
Uri devToolsServerAddress Future<void> serveAndAnnounceDevTools({
Uri devToolsServerAddress,
}) async { }) async {
if (!supportsServiceProtocol) { if (!supportsServiceProtocol) {
return; return;
} }
_devToolsLauncher ??= DevtoolsLauncher.instance; _devToolsLauncher ??= DevtoolsLauncher.instance;
if (devToolsServerAddress != null) { if (devToolsServerAddress != null) {
_devToolsLauncher.devToolsUri = devToolsServerAddress; _devToolsLauncher.devToolsUrl = devToolsServerAddress;
} else { } else {
await _devToolsLauncher.serve(); unawaited(_devToolsLauncher.serve());
}
await _devToolsLauncher.ready;
if (_reportedDebuggers) {
// Since the DevTools only just became available, we haven't had a chance to
// report their URLs yet. Do so now.
printDebuggerList(includeObservatory: false);
} }
await maybeCallDevToolsUriServiceExtension();
} }
Future<void> maybeCallDevToolsUriServiceExtension() async { Future<void> maybeCallDevToolsUriServiceExtension() async {
...@@ -1417,6 +1428,39 @@ abstract class ResidentRunner { ...@@ -1417,6 +1428,39 @@ abstract class ResidentRunner {
appFinished(); appFinished();
} }
bool _reportedDebuggers = false;
void printDebuggerList({ bool includeObservatory = true, bool includeDevtools = true }) {
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress == null) {
includeDevtools = false;
}
for (final FlutterDevice device in flutterDevices) {
if (device.vmService == null) {
continue;
}
if (includeObservatory) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on ${device.device.name} is available at: '
'${device.vmService.httpAddress}',
);
}
if (includeDevtools) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'The Flutter DevTools debugger and profiler '
'on ${device.device.name} is available at: $uri',
);
}
}
}
_reportedDebuggers = true;
}
/// Called to print help to the terminal. /// Called to print help to the terminal.
void printHelp({ @required bool details }); void printHelp({ @required bool details });
...@@ -1763,24 +1807,51 @@ String nextPlatform(String currentPlatform, FeatureFlags featureFlags) { ...@@ -1763,24 +1807,51 @@ String nextPlatform(String currentPlatform, FeatureFlags featureFlags) {
/// A launcher for the devtools debugger and analysis tool. /// A launcher for the devtools debugger and analysis tool.
abstract class DevtoolsLauncher { abstract class DevtoolsLauncher {
Uri devToolsUri; static DevtoolsLauncher get instance => context.get<DevtoolsLauncher>();
/// Serve Dart DevTools and return the host and port they are available on.
///
/// This method must return a future that is guaranteed not to fail, because it
/// will be used in unawaited contexts. It may, however, return null.
Future<DevToolsServerAddress> serve();
/// Launch a Dart DevTools process, optionally targeting a specific VM Service /// Launch a Dart DevTools process, optionally targeting a specific VM Service
/// URI if [vmServiceUri] is non-null. /// URI if [vmServiceUri] is non-null.
///
/// This method must return a future that is guaranteed not to fail, because it
/// will be used in unawaited contexts.
@visibleForTesting
Future<void> launch(Uri vmServiceUri); Future<void> launch(Uri vmServiceUri);
/// Serve Dart DevTools and return the host and port they are available on.
Future<DevToolsServerAddress> serve();
Future<void> close(); Future<void> close();
static DevtoolsLauncher get instance => context.get<DevtoolsLauncher>(); /// Returns a future that completes when the DevTools server is ready.
///
/// Completes when [devToolsUrl] is set. That can be set either directly, or
/// by calling [serve].
Future<void> get ready => _readyCompleter.future;
Completer<void> _readyCompleter = Completer<void>();
Uri get devToolsUrl => _devToolsUrl;
Uri _devToolsUrl;
set devToolsUrl(Uri value) {
assert((_devToolsUrl == null) != (value == null));
_devToolsUrl = value;
if (_devToolsUrl != null) {
_readyCompleter.complete();
} else {
_readyCompleter = Completer<void>();
}
}
/// The URL of the current DevTools server.
///
/// Returns null if [ready] is not complete.
DevToolsServerAddress get activeDevToolsServer { DevToolsServerAddress get activeDevToolsServer {
if (devToolsUri == null) { if (_devToolsUrl == null) {
return null; return null;
} }
return DevToolsServerAddress(devToolsUri.host, devToolsUri.port); return DevToolsServerAddress(devToolsUrl.host, devToolsUrl.port);
} }
} }
......
...@@ -53,6 +53,7 @@ class ColdRunner extends ResidentRunner { ...@@ -53,6 +53,7 @@ class ColdRunner extends ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async { }) async {
try { try {
...@@ -72,17 +73,17 @@ class ColdRunner extends ResidentRunner { ...@@ -72,17 +73,17 @@ class ColdRunner extends ResidentRunner {
return 1; return 1;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
// Connect to observatory. // Connect to observatory.
if (debuggingOptions.debuggingEnabled) { if (debuggingEnabled) {
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(allowExistingDdsInstance: false);
connectToServiceProtocol(
allowExistingDdsInstance: false,
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
} on String catch (message) { } on String catch (message) {
globals.printError(message); globals.printError(message);
appFailedToStart(); appFailedToStart();
...@@ -124,7 +125,6 @@ class ColdRunner extends ResidentRunner { ...@@ -124,7 +125,6 @@ class ColdRunner extends ResidentRunner {
} }
if (debuggingEnabled) { if (debuggingEnabled) {
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
} }
...@@ -144,22 +144,26 @@ class ColdRunner extends ResidentRunner { ...@@ -144,22 +144,26 @@ class ColdRunner extends ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async { }) async {
_didAttach = true; _didAttach = true;
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(
connectToServiceProtocol( getSkSLMethod: writeSkSL,
getSkSLMethod: writeSkSL, allowExistingDdsInstance: allowExistingDdsInstance,
allowExistingDdsInstance: allowExistingDdsInstance, );
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
], eagerError: true);
} on Exception catch (error) { } on Exception catch (error) {
globals.printError('Error connecting to the service protocol: $error'); globals.printError('Error connecting to the service protocol: $error');
return 2; return 2;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
await device.initLogReader(); await device.initLogReader();
} }
...@@ -170,7 +174,6 @@ class ColdRunner extends ResidentRunner { ...@@ -170,7 +174,6 @@ class ColdRunner extends ResidentRunner {
} }
} }
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
appStartedCompleter?.complete(); appStartedCompleter?.complete();
...@@ -205,35 +208,13 @@ class ColdRunner extends ResidentRunner { ...@@ -205,35 +208,13 @@ class ColdRunner extends ResidentRunner {
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
} }
commandHelp.h.print(); commandHelp.h.print(); // TODO(ianh): print different message if details is false
if (_didAttach) { if (_didAttach) {
commandHelp.d.print(); commandHelp.d.print();
} }
commandHelp.c.print(); commandHelp.c.print();
commandHelp.q.print(); commandHelp.q.print();
for (final FlutterDevice device in flutterDevices) { printDebuggerList();
final String dname = device.device.name;
if (device.vmService != null) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: '
'${device.vmService.httpAddress}',
);
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress != null) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'\nFlutter DevTools, a Flutter debugger and profiler, on '
'${device.device.name} is available at: $uri',
);
}
}
}
}
} }
@override @override
......
...@@ -174,21 +174,17 @@ class HotRunner extends ResidentRunner { ...@@ -174,21 +174,17 @@ class HotRunner extends ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async { }) async {
_didAttach = true; _didAttach = true;
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(
connectToServiceProtocol( reloadSources: _reloadSourcesService,
reloadSources: _reloadSourcesService, restart: _restartService,
restart: _restartService, compileExpression: _compileExpressionService,
compileExpression: _compileExpressionService, getSkSLMethod: writeSkSL,
getSkSLMethod: writeSkSL, allowExistingDdsInstance: allowExistingDdsInstance,
allowExistingDdsInstance: allowExistingDdsInstance, );
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
// Catches all exceptions, non-Exception objects are rethrown. // Catches all exceptions, non-Exception objects are rethrown.
} catch (error) { // ignore: avoid_catches_without_on_clauses } catch (error) { // ignore: avoid_catches_without_on_clauses
if (error is! Exception && error is! String) { if (error is! Exception && error is! String) {
...@@ -198,6 +194,13 @@ class HotRunner extends ResidentRunner { ...@@ -198,6 +194,13 @@ class HotRunner extends ResidentRunner {
return 2; return 2;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
await device.initLogReader(); await device.initLogReader();
} }
...@@ -218,7 +221,6 @@ class HotRunner extends ResidentRunner { ...@@ -218,7 +221,6 @@ class HotRunner extends ResidentRunner {
return 3; return 3;
} }
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
final Stopwatch initialUpdateDevFSsTimer = Stopwatch()..start(); final Stopwatch initialUpdateDevFSsTimer = Stopwatch()..start();
...@@ -306,6 +308,7 @@ class HotRunner extends ResidentRunner { ...@@ -306,6 +308,7 @@ class HotRunner extends ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async { }) async {
firstBuildTime = DateTime.now(); firstBuildTime = DateTime.now();
...@@ -356,6 +359,7 @@ class HotRunner extends ResidentRunner { ...@@ -356,6 +359,7 @@ class HotRunner extends ResidentRunner {
return attach( return attach(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: enableDevTools,
); );
} }
...@@ -1086,7 +1090,7 @@ class HotRunner extends ResidentRunner { ...@@ -1086,7 +1090,7 @@ class HotRunner extends ResidentRunner {
if (canHotRestart) { if (canHotRestart) {
commandHelp.R.print(); commandHelp.R.print();
} }
commandHelp.h.print(); commandHelp.h.print(); // TODO(ianh): print different message if "details" is false
if (_didAttach) { if (_didAttach) {
commandHelp.d.print(); commandHelp.d.print();
} }
...@@ -1095,26 +1099,6 @@ class HotRunner extends ResidentRunner { ...@@ -1095,26 +1099,6 @@ class HotRunner extends ResidentRunner {
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
} }
for (final FlutterDevice device in flutterDevices) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on ${device.device.name} is available at: '
'${device.vmService.httpAddress}',
);
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress != null) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'\nFlutter DevTools, a Flutter debugger and profiler, on '
'${device.device.name} is available at: $uri',
);
}
}
}
globals.printStatus(''); globals.printStatus('');
if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) { if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus('💪 Running with sound null safety 💪', emphasis: true); globals.printStatus('💪 Running with sound null safety 💪', emphasis: true);
...@@ -1127,6 +1111,8 @@ class HotRunner extends ResidentRunner { ...@@ -1127,6 +1111,8 @@ class HotRunner extends ResidentRunner {
'For more information see https://dart.dev/null-safety/unsound-null-safety', 'For more information see https://dart.dev/null-safety/unsound-null-safety',
); );
} }
globals.printStatus('');
printDebuggerList();
} }
Future<void> _evictDirtyAssets() async { Future<void> _evictDirtyAssets() async {
......
...@@ -133,6 +133,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -133,6 +133,9 @@ abstract class FlutterCommand extends Command<void> {
/// The option name for a custom DevTools server address. /// The option name for a custom DevTools server address.
static const String kDevToolsServerAddress = 'devtools-server-address'; static const String kDevToolsServerAddress = 'devtools-server-address';
/// The flag name for whether to launch the DevTools or not.
static const String kEnableDevTools = 'devtools';
/// The flag name for whether or not to use ipv6. /// The flag name for whether or not to use ipv6.
static const String ipv6Flag = 'ipv6'; static const String ipv6Flag = 'ipv6';
...@@ -327,27 +330,39 @@ abstract class FlutterCommand extends Command<void> { ...@@ -327,27 +330,39 @@ abstract class FlutterCommand extends Command<void> {
_usesPortOption = true; _usesPortOption = true;
} }
void addDevToolsOptions() { void addDevToolsOptions({@required bool verboseHelp}) {
argParser.addOption(kDevToolsServerAddress, argParser.addFlag(
kEnableDevTools,
hide: !verboseHelp,
defaultsTo: true,
help: 'Enable (or disable, with --no-$kEnableDevTools) the launching of the '
'Flutter DevTools debugger and profiler. '
'If specified, --$kDevToolsServerAddress is ignored.'
);
argParser.addOption(
kDevToolsServerAddress,
hide: !verboseHelp,
help: 'When this value is provided, the Flutter tool will not spin up a ' help: 'When this value is provided, the Flutter tool will not spin up a '
'new DevTools server instance, but instead will use the one provided ' 'new DevTools server instance, and will instead use the one provided '
'at this address.'); 'at the given address. Ignored if --no-$kEnableDevTools is specified.'
);
} }
void addDdsOptions({@required bool verboseHelp}) { void addDdsOptions({@required bool verboseHelp}) {
argParser.addOption('dds-port', argParser.addOption('dds-port',
help: 'When this value is provided, the Dart Development Service (DDS) will be ' help: 'When this value is provided, the Dart Development Service (DDS) will be '
'bound to the provided port.\nSpecifying port 0 (the default) will find ' 'bound to the provided port.\n'
'a random free port.'); 'Specifying port 0 (the default) will find a random free port.'
);
argParser.addFlag( argParser.addFlag(
'disable-dds', 'disable-dds', // TODO(ianh): this should be called `dds` and default to true (see style guide about double negatives)
hide: !verboseHelp, hide: !verboseHelp,
help: 'Disable the Dart Developer Service (DDS). This flag should only be provided' help: 'Disable the Dart Developer Service (DDS). This flag should only be provided '
' when attaching to an application with an existing DDS instance (e.g.,' 'when attaching to an application with an existing DDS instance (e.g., '
' attaching to an application currently connected to by "flutter run") or' 'attaching to an application currently connected to by "flutter run") or '
' when running certain tests.\n' 'when running certain tests.\n'
'Note: passing this flag may degrade IDE functionality if a DDS instance is not' 'Passing this flag may degrade IDE functionality if a DDS instance is not '
' already connected to the target application.' 'already connected to the target application.'
); );
} }
......
...@@ -181,8 +181,11 @@ void main() { ...@@ -181,8 +181,11 @@ void main() {
const String outputDill = '/tmp/output.dill'; const String outputDill = '/tmp/output.dill';
final MockHotRunner mockHotRunner = MockHotRunner(); final MockHotRunner mockHotRunner = MockHotRunner();
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunner.exited).thenReturn(false); when(mockHotRunner.exited).thenReturn(false);
when(mockHotRunner.isWaitingForObservatory).thenReturn(false); when(mockHotRunner.isWaitingForObservatory).thenReturn(false);
...@@ -313,8 +316,11 @@ void main() { ...@@ -313,8 +316,11 @@ void main() {
.thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]); .thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)) when(portForwarder.unforward(any))
.thenAnswer((_) async {}); .thenAnswer((_) async {});
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunnerFactory.build( when(mockHotRunnerFactory.build(
any, any,
target: anyNamed('target'), target: anyNamed('target'),
...@@ -397,8 +403,11 @@ void main() { ...@@ -397,8 +403,11 @@ void main() {
.thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]); .thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)) when(portForwarder.unforward(any))
.thenAnswer((_) async {}); .thenAnswer((_) async {});
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunnerFactory.build( when(mockHotRunnerFactory.build(
any, any,
target: anyNamed('target'), target: anyNamed('target'),
......
...@@ -42,7 +42,9 @@ void main() { ...@@ -42,7 +42,9 @@ void main() {
final int exitCode = await ColdRunner(devices, final int exitCode = await ColdRunner(devices,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).attach(); ).attach(
enableDevTools: false,
);
expect(exitCode, 2); expect(exitCode, 2);
}); });
...@@ -90,7 +92,9 @@ void main() { ...@@ -90,7 +92,9 @@ void main() {
applicationBinary: applicationBinary, applicationBinary: applicationBinary,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).run(); ).run(
enableDevTools: false,
);
expect(result, 1); expect(result, 1);
verify(mockFlutterDevice.runCold( verify(mockFlutterDevice.runCold(
......
...@@ -524,7 +524,9 @@ void main() { ...@@ -524,7 +524,9 @@ void main() {
final int exitCode = await HotRunner(devices, final int exitCode = await HotRunner(devices,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).attach(); ).attach(
enableDevTools: false,
);
expect(exitCode, 2); expect(exitCode, 2);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
......
...@@ -289,7 +289,7 @@ void main() { ...@@ -289,7 +289,7 @@ void main() {
}); });
testUsingContext('devToolsServerAddress returns parsed uri', () async { testUsingContext('devToolsServerAddress returns parsed uri', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(); final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(verboseHelp: false);
await createTestCommandRunner(command).run(<String>[ await createTestCommandRunner(command).run(<String>[
'dummy', 'dummy',
'--${FlutterCommand.kDevToolsServerAddress}', '--${FlutterCommand.kDevToolsServerAddress}',
...@@ -299,7 +299,7 @@ void main() { ...@@ -299,7 +299,7 @@ void main() {
}); });
testUsingContext('devToolsServerAddress returns null for bad input', () async { testUsingContext('devToolsServerAddress returns null for bad input', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(); final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(verboseHelp: false);
final CommandRunner<void> runner = createTestCommandRunner(command); final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[ await runner.run(<String>[
'dummy', 'dummy',
......
...@@ -341,6 +341,7 @@ class TestRunner extends Mock implements ResidentRunner { ...@@ -341,6 +341,7 @@ class TestRunner extends Mock implements ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async => null; }) async => null;
...@@ -349,5 +350,6 @@ class TestRunner extends Mock implements ResidentRunner { ...@@ -349,5 +350,6 @@ class TestRunner extends Mock implements ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async => null; }) async => null;
} }
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