run_cold.dart 6.22 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.dart' as globals;
16
import 'resident_devtools_handler.dart';
17
import 'resident_runner.dart';
18
import 'tracing.dart';
19
import 'vmservice.dart';
20

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

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

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

  @override
  bool get canHotRestart => false;

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

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

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

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

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

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

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

131 132
    appStartedCompleter?.complete();

133
    writeVmServiceFile();
134

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

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

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

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

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

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

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

201
    await stopEchoingDeviceLog();
202 203
  }

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

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