run_cold.dart 6.41 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7 8
import 'dart:async';

9
import 'package:meta/meta.dart';
10

11
import 'base/common.dart';
12
import 'base/file_system.dart';
13
import 'build_info.dart';
14
import 'device.dart';
15
import 'globals_null_migrated.dart' as globals;
16
import 'resident_devtools_handler.dart';
17
import 'resident_runner.dart';
18
import 'tracing.dart';
19
import 'vmservice.dart';
20

21
const String kFlutterTestOutputsDirEnvName = 'FLUTTER_TEST_OUTPUTS_DIR';
22 23
class ColdRunner extends ResidentRunner {
  ColdRunner(
24
    List<FlutterDevice> devices, {
25
    @required String target,
26
    @required DebuggingOptions debuggingOptions,
27
    this.traceStartup = false,
28
    this.awaitFirstFrameWhenTracing = true,
29
    this.applicationBinary,
30
    bool ipv6 = false,
31
    bool stayResident = true,
32
    bool machine = false,
33
    ResidentDevtoolsHandlerFactory devtoolsHandler = createDefaultHandler,
34 35 36 37 38 39 40 41
  }) : super(
          devices,
          target: target,
          debuggingOptions: debuggingOptions,
          hotMode: false,
          stayResident: stayResident,
          ipv6: ipv6,
          machine: machine,
42
          devtoolsHandler: devtoolsHandler,
43
        );
44

45
  final bool traceStartup;
46
  final bool awaitFirstFrameWhenTracing;
47
  final File applicationBinary;
48
  bool _didAttach = false;
49

50 51 52 53 54 55
  @override
  bool get canHotReload => false;

  @override
  bool get canHotRestart => false;

56
  @override
57
  Future<int> run({
58
    Completer<DebugConnectionInfo> connectionInfoCompleter,
59
    Completer<void> appStartedCompleter,
60
    bool enableDevTools = false,
61
    String route,
62
  }) async {
63 64 65 66 67 68 69 70 71 72
    try {
      for (final FlutterDevice device in flutterDevices) {
        final int result = await device.runCold(
          coldRunner: this,
          route: route,
        );
        if (result != 0) {
          appFailedToStart();
          return result;
        }
73
      }
74 75 76 77
    } on Exception catch (err) {
      globals.printError(err.toString());
      appFailedToStart();
      return 1;
78 79
    }

80
    // Connect to observatory.
81
    if (debuggingEnabled) {
82
      try {
83
        await connectToServiceProtocol(allowExistingDdsInstance: false);
84
      } on String catch (message) {
85
        globals.printError(message);
86
        appFailedToStart();
87 88 89
        return 2;
      }
    }
90

91 92 93 94 95 96 97 98
    if (enableDevTools && debuggingEnabled) {
      // The method below is guaranteed never to return a failing future.
      unawaited(residentDevtoolsHandler.serveAndAnnounceDevTools(
        devToolsServerAddress: debuggingOptions.devToolsServerAddress,
        flutterDevices: flutterDevices,
      ));
    }

99 100
    if (flutterDevices.first.observatoryUris != null) {
      // For now, only support one debugger connection.
101
      connectionInfoCompleter?.complete(DebugConnectionInfo(
102 103
        httpUri: flutterDevices.first.vmService.httpAddress,
        wsUri: flutterDevices.first.vmService.wsAddress,
104 105
      ));
    }
106

107
    globals.printTrace('Application running.');
108

109
    for (final FlutterDevice device in flutterDevices) {
110
      if (device.vmService == null) {
111
        continue;
112
      }
113
      await device.initLogReader();
114
      globals.printTrace('Connected to ${device.device.name}');
115
    }
116

117 118 119
    if (traceStartup) {
      // Only trace startup for the first device.
      final FlutterDevice device = flutterDevices.first;
120
      if (device.vmService != null) {
121
        globals.printStatus('Tracing startup on ${device.device.name}.');
122
        final String outputPath = globals.platform.environment[kFlutterTestOutputsDirEnvName] ?? getBuildDirectory();
123
        await downloadStartupTrace(
124
          device.vmService,
125
          awaitFirstFrame: awaitFirstFrameWhenTracing,
126
          logger: globals.logger,
127
          output: globals.fs.directory(outputPath),
128
        );
129
      }
130
      appFinished();
131 132
    }

133 134
    appStartedCompleter?.complete();

135
    writeVmServiceFile();
136

137
    if (stayResident && !traceStartup) {
138
      return waitForAppToFinish();
139
    }
140 141
    await cleanupAtFinish();
    return 0;
142 143
  }

144 145 146 147
  @override
  Future<int> attach({
    Completer<DebugConnectionInfo> connectionInfoCompleter,
    Completer<void> appStartedCompleter,
148
    bool allowExistingDdsInstance = false,
149
    bool enableDevTools = false,
150 151 152
  }) async {
    _didAttach = true;
    try {
153 154 155 156
      await connectToServiceProtocol(
        getSkSLMethod: writeSkSL,
        allowExistingDdsInstance: allowExistingDdsInstance,
      );
157
    } on Exception catch (error) {
158
      globals.printError('Error connecting to the service protocol: $error');
159 160
      return 2;
    }
161

162
    for (final FlutterDevice device in flutterDevices) {
163
      await device.initLogReader();
164
    }
165
    for (final FlutterDevice device in flutterDevices) {
166 167
      final List<FlutterView> views = await device.vmService.getFlutterViews();
      for (final FlutterView view in views) {
168
        globals.printTrace('Connected to $view.');
169 170
      }
    }
171

172 173 174 175 176 177 178
    if (enableDevTools && debuggingEnabled) {
      // The method below is guaranteed never to return a failing future.
      unawaited(residentDevtoolsHandler.serveAndAnnounceDevTools(
        devToolsServerAddress: debuggingOptions.devToolsServerAddress,
        flutterDevices: flutterDevices,
      ));
    }
179

180 181 182 183 184 185 186 187
    appStartedCompleter?.complete();
    if (stayResident) {
      return waitForAppToFinish();
    }
    await cleanupAtFinish();
    return 0;
  }

188
  @override
189
  Future<void> cleanupAfterSignal() async {
190
    await stopEchoingDeviceLog();
191 192 193
    if (_didAttach) {
      appFinished();
    }
194
    await exitApp();
195 196
  }

197
  @override
198
  Future<void> cleanupAtFinish() async {
199
    for (final FlutterDevice flutterDevice in flutterDevices) {
200
      await flutterDevice.device.dispose();
201 202
    }

203
    await stopEchoingDeviceLog();
204 205
  }

206
  @override
207
  void printHelp({ @required bool details }) {
208
    globals.printStatus('Flutter run key commands.');
209 210
    if (details) {
      printHelpDetails();
211
    }
212
    commandHelp.h.print(); // TODO(ianh): print different message if details is false
213
    if (_didAttach) {
214
      commandHelp.d.print();
215
    }
216
    commandHelp.c.print();
217
    commandHelp.q.print();
218
    printDebuggerList();
219
  }
220 221

  @override
222
  Future<void> preExit() async {
223
    for (final FlutterDevice device in flutterDevices) {
224
      // If we're running in release mode, stop the app using the device logic.
225
      if (device.vmService == null) {
226
        await device.device.stopApp(device.package, userIdentifier: device.userIdentifier);
227
      }
228
    }
229
    await super.preExit();
230
  }
231
}