run_cold.dart 6.61 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 'base/logger.dart';
14
import 'build_info.dart';
15
import 'device.dart';
16
import 'globals.dart' as globals;
17
import 'resident_devtools_handler.dart';
18
import 'resident_runner.dart';
19
import 'tracing.dart';
20
import 'vmservice.dart';
21

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

47
  final bool traceStartup;
48
  final bool awaitFirstFrameWhenTracing;
49
  final File applicationBinary;
50
  final bool multidexEnabled;
51
  bool _didAttach = false;
52

53 54 55 56
  @override
  bool get canHotReload => false;

  @override
57 58 59 60
  Logger get logger => globals.logger;

  @override
  FileSystem get fileSystem => globals.fs;
61

62
  @override
63
  Future<int> run({
64
    Completer<DebugConnectionInfo> connectionInfoCompleter,
65
    Completer<void> appStartedCompleter,
66
    bool enableDevTools = false,
67
    String route,
68
  }) async {
69 70 71 72 73 74 75 76 77 78
    try {
      for (final FlutterDevice device in flutterDevices) {
        final int result = await device.runCold(
          coldRunner: this,
          route: route,
        );
        if (result != 0) {
          appFailedToStart();
          return result;
        }
79
      }
80 81
    } on Exception catch (err, stack) {
      globals.printError('$err\n$stack');
82 83
      appFailedToStart();
      return 1;
84 85
    }

86
    // Connect to observatory.
87
    if (debuggingEnabled) {
88
      try {
89
        await connectToServiceProtocol(allowExistingDdsInstance: false);
90
      } on String catch (message) {
91
        globals.printError(message);
92
        appFailedToStart();
93 94 95
        return 2;
      }
    }
96

97 98 99 100 101 102 103 104
    if (enableDevTools && debuggingEnabled) {
      // The method below is guaranteed never to return a failing future.
      unawaited(residentDevtoolsHandler.serveAndAnnounceDevTools(
        devToolsServerAddress: debuggingOptions.devToolsServerAddress,
        flutterDevices: flutterDevices,
      ));
    }

105 106
    if (flutterDevices.first.observatoryUris != null) {
      // For now, only support one debugger connection.
107
      connectionInfoCompleter?.complete(DebugConnectionInfo(
108 109
        httpUri: flutterDevices.first.vmService.httpAddress,
        wsUri: flutterDevices.first.vmService.wsAddress,
110 111
      ));
    }
112

113
    globals.printTrace('Application running.');
114

115
    for (final FlutterDevice device in flutterDevices) {
116
      if (device.vmService == null) {
117
        continue;
118
      }
119
      await device.initLogReader();
120
      globals.printTrace('Connected to ${device.device.name}');
121
    }
122

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

139 140
    appStartedCompleter?.complete();

141
    writeVmServiceFile();
142

143
    if (stayResident && !traceStartup) {
144
      return waitForAppToFinish();
145
    }
146 147
    await cleanupAtFinish();
    return 0;
148 149
  }

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

168
    for (final FlutterDevice device in flutterDevices) {
169
      await device.initLogReader();
170
    }
171
    for (final FlutterDevice device in flutterDevices) {
172 173
      final List<FlutterView> views = await device.vmService.getFlutterViews();
      for (final FlutterView view in views) {
174
        globals.printTrace('Connected to $view.');
175 176
      }
    }
177

178 179 180 181 182 183 184
    if (enableDevTools && debuggingEnabled) {
      // The method below is guaranteed never to return a failing future.
      unawaited(residentDevtoolsHandler.serveAndAnnounceDevTools(
        devToolsServerAddress: debuggingOptions.devToolsServerAddress,
        flutterDevices: flutterDevices,
      ));
    }
185

186 187 188 189 190 191 192 193
    appStartedCompleter?.complete();
    if (stayResident) {
      return waitForAppToFinish();
    }
    await cleanupAtFinish();
    return 0;
  }

194
  @override
195
  Future<void> cleanupAfterSignal() async {
196
    await stopEchoingDeviceLog();
197 198 199
    if (_didAttach) {
      appFinished();
    }
200
    await exitApp();
201 202
  }

203
  @override
204
  Future<void> cleanupAtFinish() async {
205
    for (final FlutterDevice flutterDevice in flutterDevices) {
206
      await flutterDevice.device.dispose();
207 208
    }

209
    await residentDevtoolsHandler.shutdown();
210
    await stopEchoingDeviceLog();
211 212
  }

213
  @override
214
  void printHelp({ @required bool details }) {
215
    globals.printStatus('Flutter run key commands.');
216 217
    if (details) {
      printHelpDetails();
218 219 220
      commandHelp.hWithDetails.print();
    } else {
      commandHelp.hWithoutDetails.print();
221
    }
222
    if (_didAttach) {
223
      commandHelp.d.print();
224
    }
225
    commandHelp.c.print();
226
    commandHelp.q.print();
227
    printDebuggerList();
228
  }
229 230

  @override
231
  Future<void> preExit() async {
232
    for (final FlutterDevice device in flutterDevices) {
233
      // If we're running in release mode, stop the app using the device logic.
234
      if (device.vmService == null) {
235
        await device.device.stopApp(device.package, userIdentifier: device.userIdentifier);
236
      }
237
    }
238
    await super.preExit();
239
  }
240
}