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

import 'dart:async';

7
import 'base/file_system.dart';
8
import 'base/logger.dart';
9
import 'build_info.dart';
10
import 'globals.dart' as globals;
11
import 'resident_runner.dart';
12
import 'tracing.dart';
13
import 'vmservice.dart';
14

15
const String kFlutterTestOutputsDirEnvName = 'FLUTTER_TEST_OUTPUTS_DIR';
16 17
class ColdRunner extends ResidentRunner {
  ColdRunner(
18
    super.flutterDevices, {
19 20
    required super.target,
    required super.debuggingOptions,
21
    this.traceStartup = false,
22
    this.awaitFirstFrameWhenTracing = true,
23
    this.applicationBinary,
24
    this.multidexEnabled = false,
25 26 27 28
    bool super.ipv6 = false,
    super.stayResident,
    super.machine,
    super.devtoolsHandler,
29 30 31
  }) : super(
          hotMode: false,
        );
32

33
  final bool traceStartup;
34
  final bool awaitFirstFrameWhenTracing;
35
  final File? applicationBinary;
36
  final bool multidexEnabled;
37
  bool _didAttach = false;
38

39 40 41 42
  @override
  bool get canHotReload => false;

  @override
43 44 45 46
  Logger get logger => globals.logger;

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

48
  @override
49
  Future<int> run({
50 51
    Completer<DebugConnectionInfo>? connectionInfoCompleter,
    Completer<void>? appStartedCompleter,
52
    bool enableDevTools = false,
53
    String? route,
54
  }) async {
55
    try {
56 57
      for (final FlutterDevice? device in flutterDevices) {
        final int result = await device!.runCold(
58 59 60 61 62 63 64
          coldRunner: this,
          route: route,
        );
        if (result != 0) {
          appFailedToStart();
          return result;
        }
65
      }
66 67
    } on Exception catch (err, stack) {
      globals.printError('$err\n$stack');
68 69
      appFailedToStart();
      return 1;
70 71
    }

72
    // Connect to the VM Service.
73
    if (debuggingEnabled) {
74
      try {
75
        await connectToServiceProtocol(allowExistingDdsInstance: false);
76 77
      } on Exception catch (exception) {
        globals.printError(exception.toString());
78
        appFailedToStart();
79 80 81
        return 2;
      }
    }
82

83 84 85 86 87 88 89 90 91 92 93
    if (debuggingEnabled) {
      if (enableDevTools) {
        // The method below is guaranteed never to return a failing future.
        unawaited(residentDevtoolsHandler!.serveAndAnnounceDevTools(
          devToolsServerAddress: debuggingOptions.devToolsServerAddress,
          flutterDevices: flutterDevices,
        ));
      }
      if (debuggingOptions.serveObservatory) {
        await enableObservatory();
      }
94 95
    }

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

104
    globals.printTrace('Application running.');
105

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

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

130 131
    appStartedCompleter?.complete();

132
    writeVmServiceFile();
133

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

141 142
  @override
  Future<int> attach({
143 144
    Completer<DebugConnectionInfo>? connectionInfoCompleter,
    Completer<void>? appStartedCompleter,
145
    bool allowExistingDdsInstance = false,
146
    bool enableDevTools = false,
147
    bool needsFullRestart = true,
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 161
    for (final FlutterDevice? device in flutterDevices) {
      await device!.initLogReader();
162
    }
163 164
    for (final FlutterDevice? device in flutterDevices) {
      final List<FlutterView> views = await device!.vmService!.getFlutterViews();
165
      for (final FlutterView view in views) {
166
        globals.printTrace('Connected to $view.');
167 168
      }
    }
169

170 171 172 173 174 175 176 177 178 179 180
    if (debuggingEnabled) {
      if (enableDevTools) {
        // The method below is guaranteed never to return a failing future.
        unawaited(residentDevtoolsHandler!.serveAndAnnounceDevTools(
          devToolsServerAddress: debuggingOptions.devToolsServerAddress,
          flutterDevices: flutterDevices,
        ));
      }
      if (debuggingOptions.serveObservatory) {
        await enableObservatory();
      }
181
    }
182

183 184 185 186 187 188 189 190
    appStartedCompleter?.complete();
    if (stayResident) {
      return waitForAppToFinish();
    }
    await cleanupAtFinish();
    return 0;
  }

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

200
  @override
201
  Future<void> cleanupAtFinish() async {
202 203
    for (final FlutterDevice? flutterDevice in flutterDevices) {
      await flutterDevice!.device!.dispose();
204 205
    }

206
    await residentDevtoolsHandler!.shutdown();
207
    await stopEchoingDeviceLog();
208 209
  }

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

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