blob: 177942604f08f5459addb6806630b2bb1a574f9e [file] [log] [blame]
Ian Hickson449f4a62019-11-27 15:04:02 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
Devon Carew3ba17132016-06-07 12:13:35 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
Devon Carew3ba17132016-06-07 12:13:35 -07006
Ian Hicksona33b70e2016-10-31 21:57:59 -07007import 'package:meta/meta.dart';
John McCutchandd52b7c2016-10-20 09:40:00 -07008
Todd Volkert8bb27032017-01-06 16:51:44 -08009import 'base/file_system.dart';
Devon Carew3ba17132016-06-07 12:13:35 -070010import 'device.dart';
Jonah Williamsee7a37f2020-01-06 11:04:20 -080011import 'globals.dart' as globals;
John McCutchan81b4e822016-08-05 12:04:33 -070012import 'resident_runner.dart';
Todd Volkert454db9d2017-11-09 21:45:31 -080013import 'tracing.dart';
Jonah Williams830c0df2019-01-18 10:09:44 -080014import 'vmservice.dart';
Devon Carew3ba17132016-06-07 12:13:35 -070015
Alexandre Ardhuin79b5e5b2018-11-19 10:37:55 +010016// TODO(mklim): Test this, flutter/flutter#23031.
Ian Hicksonf888bbe2017-01-27 01:03:04 -080017class ColdRunner extends ResidentRunner {
18 ColdRunner(
Zachary Anderson0770c3c2017-04-26 21:49:38 -070019 List<FlutterDevice> devices, {
John McCutchan81b4e822016-08-05 12:04:33 -070020 String target,
21 DebuggingOptions debuggingOptions,
Alexandre Ardhuin09276be2018-06-05 08:50:40 +020022 this.traceStartup = false,
Ian Hickson31a96262019-01-19 00:31:05 -080023 this.awaitFirstFrameWhenTracing = true,
Ian Hicksonf888bbe2017-01-27 01:03:04 -080024 this.applicationBinary,
Alexandre Ardhuin09276be2018-06-05 08:50:40 +020025 bool ipv6 = false,
Jonah Williamsadf45d12019-07-09 13:10:26 -070026 bool stayResident = true,
Zachary Anderson0770c3c2017-04-26 21:49:38 -070027 }) : super(devices,
John McCutchan81b4e822016-08-05 12:04:33 -070028 target: target,
29 debuggingOptions: debuggingOptions,
Jonah Williamsadf45d12019-07-09 13:10:26 -070030 hotMode: false,
Todd Volkerte792c6b2017-11-21 20:12:21 -080031 stayResident: stayResident,
32 ipv6: ipv6);
Devon Carew3ba17132016-06-07 12:13:35 -070033
Devon Carew14483582016-08-09 14:38:13 -070034 final bool traceStartup;
Ian Hickson31a96262019-01-19 00:31:05 -080035 final bool awaitFirstFrameWhenTracing;
Sigurd Meldgaard2d3a5c72018-07-20 08:00:30 +020036 final File applicationBinary;
Jonah Williams830c0df2019-01-18 10:09:44 -080037 bool _didAttach = false;
Devon Carew3ba17132016-06-07 12:13:35 -070038
Devon Carew30505ae2016-08-05 21:09:42 -070039 @override
Jonah Williams7b150f82019-07-12 08:48:14 -070040 bool get canHotReload => false;
41
42 @override
43 bool get canHotRestart => false;
44
45 @override
Devon Carew1d018382016-08-09 09:03:39 -070046 Future<int> run({
Devon Carew6b1597d2016-08-11 09:51:19 -070047 Completer<DebugConnectionInfo> connectionInfoCompleter,
Danny Tuppenyed9afbb2018-07-19 13:38:29 +010048 Completer<void> appStartedCompleter,
Devon Carew1d018382016-08-09 09:03:39 -070049 String route,
Devon Carew3ba17132016-06-07 12:13:35 -070050 }) async {
Zachary Anderson0770c3c2017-04-26 21:49:38 -070051 final bool prebuiltMode = applicationBinary != null;
John McCutchanca8070f2016-09-28 08:46:16 -070052 if (!prebuiltMode) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -080053 if (!globals.fs.isFileSync(mainPath)) {
John McCutchan6a63af42017-01-10 07:59:55 -080054 String message = 'Tried to run $mainPath, but that file does not exist.';
Zachary Andersone2340c62019-09-13 14:51:35 -070055 if (target == null) {
John McCutchanca8070f2016-09-28 08:46:16 -070056 message += '\nConsider using the -t option to specify the Dart file to start.';
Zachary Andersone2340c62019-09-13 14:51:35 -070057 }
Jonah Williamsee7a37f2020-01-06 11:04:20 -080058 globals.printError(message);
John McCutchanca8070f2016-09-28 08:46:16 -070059 return 1;
60 }
Devon Carew3ba17132016-06-07 12:13:35 -070061 }
62
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +010063 for (final FlutterDevice device in flutterDevices) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -070064 final int result = await device.runCold(
65 coldRunner: this,
66 route: route,
Zachary Anderson0770c3c2017-04-26 21:49:38 -070067 );
Zachary Andersone2340c62019-09-13 14:51:35 -070068 if (result != 0) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -070069 return result;
Zachary Andersone2340c62019-09-13 14:51:35 -070070 }
Devon Carewf29dd4f2017-04-20 07:13:35 -070071 }
72
Dan Rubelfcf41fc2016-11-28 16:15:19 -050073 // Connect to observatory.
Ian Hickson31a96262019-01-19 00:31:05 -080074 if (debuggingOptions.debuggingEnabled) {
75 try {
76 await connectToServiceProtocol();
77 } on String catch (message) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -080078 globals.printError(message);
Ian Hickson31a96262019-01-19 00:31:05 -080079 return 2;
80 }
81 }
Dan Rubela9584e12016-11-30 20:29:04 -050082
Zachary Anderson0770c3c2017-04-26 21:49:38 -070083 if (flutterDevices.first.observatoryUris != null) {
84 // For now, only support one debugger connection.
Alexandre Ardhuind927c932018-09-12 08:29:29 +020085 connectionInfoCompleter?.complete(DebugConnectionInfo(
Jonah Williams598f2ab2019-11-27 13:11:04 -080086 httpUri: flutterDevices.first.vmService.httpAddress,
87 wsUri: flutterDevices.first.vmService.wsAddress,
Dan Rubela9584e12016-11-30 20:29:04 -050088 ));
Devon Carew3ba17132016-06-07 12:13:35 -070089 }
90
Jonah Williamsee7a37f2020-01-06 11:04:20 -080091 globals.printTrace('Application running.');
Devon Carew3ba17132016-06-07 12:13:35 -070092
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +010093 for (final FlutterDevice device in flutterDevices) {
Jonah Williams598f2ab2019-11-27 13:11:04 -080094 if (device.vmService == null) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -070095 continue;
Zachary Andersone2340c62019-09-13 14:51:35 -070096 }
Jonah Williams37787982020-03-10 11:34:40 -070097 await device.initLogReader();
Zachary Anderson0770c3c2017-04-26 21:49:38 -070098 await device.refreshViews();
Jonah Williamsee7a37f2020-01-06 11:04:20 -080099 globals.printTrace('Connected to ${device.device.name}');
Jason Simmons073f64d2016-08-18 06:38:35 -0700100 }
John McCutchan3a012b32016-08-17 09:01:04 -0700101
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700102 if (traceStartup) {
103 // Only trace startup for the first device.
104 final FlutterDevice device = flutterDevices.first;
Jonah Williams598f2ab2019-11-27 13:11:04 -0800105 if (device.vmService != null) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800106 globals.printStatus('Tracing startup on ${device.device.name}.');
Ian Hickson31a96262019-01-19 00:31:05 -0800107 await downloadStartupTrace(
Jonah Williams598f2ab2019-11-27 13:11:04 -0800108 device.vmService,
Ian Hickson31a96262019-01-19 00:31:05 -0800109 awaitFirstFrame: awaitFirstFrameWhenTracing,
110 );
Yegora0aa0ed2016-08-09 14:12:15 -0700111 }
John McCutchan81b4e822016-08-05 12:04:33 -0700112 appFinished();
Devon Carew3ba17132016-06-07 12:13:35 -0700113 }
114
Dan Rubel1f1adca2016-11-03 14:29:56 -0400115 appStartedCompleter?.complete();
116
Jonah Williamse22d4aa2019-10-15 13:05:47 -0700117 writeVmserviceFile();
118
Zachary Andersone2340c62019-09-13 14:51:35 -0700119 if (stayResident && !traceStartup) {
Ian Hicksonf888bbe2017-01-27 01:03:04 -0800120 return waitForAppToFinish();
Zachary Andersone2340c62019-09-13 14:51:35 -0700121 }
Ian Hicksonf888bbe2017-01-27 01:03:04 -0800122 await cleanupAtFinish();
123 return 0;
Devon Carew3ba17132016-06-07 12:13:35 -0700124 }
125
John McCutchan81b4e822016-08-05 12:04:33 -0700126 @override
Jonah Williams830c0df2019-01-18 10:09:44 -0800127 Future<int> attach({
128 Completer<DebugConnectionInfo> connectionInfoCompleter,
129 Completer<void> appStartedCompleter,
130 }) async {
131 _didAttach = true;
132 try {
133 await connectToServiceProtocol();
Zachary Anderson6c408a02020-03-06 10:22:12 -0800134 } on Exception catch (error) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800135 globals.printError('Error connecting to the service protocol: $error');
Emmanuel Garcia80ee3dd2019-08-02 16:02:02 -0700136 // https://github.com/flutter/flutter/issues/33050
137 // TODO(blasten): Remove this check once https://issuetracker.google.com/issues/132325318 has been fixed.
138 if (await hasDeviceRunningAndroidQ(flutterDevices) &&
139 error.toString().contains(kAndroidQHttpConnectionClosedExp)) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800140 globals.printStatus('🔨 If you are using an emulator running Android Q Beta, consider using an emulator running API level 29 or lower.');
141 globals.printStatus('Learn more about the status of this issue on https://issuetracker.google.com/issues/132325318');
Emmanuel Garcia80ee3dd2019-08-02 16:02:02 -0700142 }
Jonah Williams830c0df2019-01-18 10:09:44 -0800143 return 2;
144 }
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100145 for (final FlutterDevice device in flutterDevices) {
Jonah Williams37787982020-03-10 11:34:40 -0700146 await device.initLogReader();
Jonah Williams830c0df2019-01-18 10:09:44 -0800147 }
148 await refreshViews();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100149 for (final FlutterDevice device in flutterDevices) {
150 for (final FlutterView view in device.views) {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800151 globals.printTrace('Connected to $view.');
Jonah Williams830c0df2019-01-18 10:09:44 -0800152 }
153 }
Jonah Williams830c0df2019-01-18 10:09:44 -0800154 appStartedCompleter?.complete();
155 if (stayResident) {
156 return waitForAppToFinish();
157 }
158 await cleanupAtFinish();
159 return 0;
160 }
161
162 @override
Alexandre Ardhuin2d3ff102018-10-05 07:54:56 +0200163 Future<void> cleanupAfterSignal() async {
John McCutchan81b4e822016-08-05 12:04:33 -0700164 await stopEchoingDeviceLog();
Jonah Williams830c0df2019-01-18 10:09:44 -0800165 if (_didAttach) {
166 appFinished();
Jonah Williams830c0df2019-01-18 10:09:44 -0800167 }
Zachary Anderson55557252019-06-06 11:16:19 -0700168 await exitApp();
Devon Carew3ba17132016-06-07 12:13:35 -0700169 }
170
John McCutchan81b4e822016-08-05 12:04:33 -0700171 @override
Alexandre Ardhuin2d3ff102018-10-05 07:54:56 +0200172 Future<void> cleanupAtFinish() async {
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100173 for (final FlutterDevice flutterDevice in flutterDevices) {
Zachary Anderson99684ce2019-12-05 08:48:00 -0800174 await flutterDevice.device.dispose();
Christopher Fujino428d7d72019-10-15 12:50:44 -0700175 }
176
John McCutchan81b4e822016-08-05 12:04:33 -0700177 await stopEchoingDeviceLog();
Devon Carew3ba17132016-06-07 12:13:35 -0700178 }
179
John McCutchan81b4e822016-08-05 12:04:33 -0700180 @override
Ian Hicksona33b70e2016-10-31 21:57:59 -0700181 void printHelp({ @required bool details }) {
Zachary Anderson23ce1922020-01-09 08:18:03 -0800182 globals.printStatus('Flutter run key commands.');
183 if (supportsServiceProtocol) {
184 if (details) {
185 printHelpDetails();
186 }
187 }
Zachary Anderson4d096c42020-01-10 16:53:01 -0800188 commandHelp.h.print();
Zachary Anderson23ce1922020-01-09 08:18:03 -0800189 if (_didAttach) {
Zachary Anderson4d096c42020-01-10 16:53:01 -0800190 commandHelp.d.print();
Zachary Anderson23ce1922020-01-09 08:18:03 -0800191 }
Levi Lesches149e0622020-02-06 18:53:04 -0500192 commandHelp.c.print();
Zachary Anderson4d096c42020-01-10 16:53:01 -0800193 commandHelp.q.print();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100194 for (final FlutterDevice device in flutterDevices) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700195 final String dname = device.device.name;
Jonah Williams598f2ab2019-11-27 13:11:04 -0800196 if (device.vmService != null) {
Zachary Anderson23ce1922020-01-09 08:18:03 -0800197 // Caution: This log line is parsed by device lab tests.
198 globals.printStatus(
199 'An Observatory debugger and profiler on $dname is available at: '
200 '${device.vmService.httpAddress}',
201 );
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700202 }
203 }
Devon Carew3ba17132016-06-07 12:13:35 -0700204 }
Devon Carewe6da16b2016-11-01 13:01:03 -0700205
206 @override
Zachary Anderson55557252019-06-06 11:16:19 -0700207 Future<void> preExit() async {
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100208 for (final FlutterDevice device in flutterDevices) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700209 // If we're running in release mode, stop the app using the device logic.
Jonah Williams598f2ab2019-11-27 13:11:04 -0800210 if (device.vmService == null) {
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700211 await device.device.stopApp(device.package);
Zachary Andersone2340c62019-09-13 14:51:35 -0700212 }
Zachary Anderson0770c3c2017-04-26 21:49:38 -0700213 }
Jonah Williams6d378672019-09-11 12:57:43 -0700214 await super.preExit();
Devon Carewe6da16b2016-11-01 13:01:03 -0700215 }
Devon Carew3ba17132016-06-07 12:13:35 -0700216}