run_cold.dart 5.91 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 'package:meta/meta.dart';
8

9
import 'base/file_system.dart';
10
import 'build_info.dart';
11
import 'device.dart';
12
import 'globals.dart' as globals;
13
import 'resident_runner.dart';
14
import 'tracing.dart';
15
import 'vmservice.dart';
16

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

38
  final bool traceStartup;
39
  final bool awaitFirstFrameWhenTracing;
40
  final File applicationBinary;
41
  bool _didAttach = false;
42

43 44 45 46 47 48
  @override
  bool get canHotReload => false;

  @override
  bool get canHotRestart => false;

49
  @override
50
  Future<int> run({
51
    Completer<DebugConnectionInfo> connectionInfoCompleter,
52
    Completer<void> appStartedCompleter,
53
    String route,
54
  }) async {
55
    final bool prebuiltMode = applicationBinary != null;
56
    if (!prebuiltMode) {
57
      if (!globals.fs.isFileSync(mainPath)) {
58
        String message = 'Tried to run $mainPath, but that file does not exist.';
59
        if (target == null) {
60
          message += '\nConsider using the -t option to specify the Dart file to start.';
61
        }
62
        globals.printError(message);
63 64
        return 1;
      }
65 66
    }

67 68 69 70 71 72 73 74 75 76
    try {
      for (final FlutterDevice device in flutterDevices) {
        final int result = await device.runCold(
          coldRunner: this,
          route: route,
        );
        if (result != 0) {
          appFailedToStart();
          return result;
        }
77
      }
78 79 80 81
    } on Exception catch (err) {
      globals.printError(err.toString());
      appFailedToStart();
      return 1;
82 83
    }

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

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

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

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

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

128 129
    appStartedCompleter?.complete();

130 131
    writeVmserviceFile();

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

139 140 141 142 143 144 145
  @override
  Future<int> attach({
    Completer<DebugConnectionInfo> connectionInfoCompleter,
    Completer<void> appStartedCompleter,
  }) async {
    _didAttach = true;
    try {
146 147 148
      await connectToServiceProtocol(
        getSkSLMethod: writeSkSL,
      );
149
    } on Exception catch (error) {
150
      globals.printError('Error connecting to the service protocol: $error');
151 152
      return 2;
    }
153
    for (final FlutterDevice device in flutterDevices) {
154
      await device.initLogReader();
155
    }
156
    for (final FlutterDevice device in flutterDevices) {
157 158
      final List<FlutterView> views = await device.vmService.getFlutterViews();
      for (final FlutterView view in views) {
159
        globals.printTrace('Connected to $view.');
160 161 162 163 164 165 166 167 168 169
      }
    }
    appStartedCompleter?.complete();
    if (stayResident) {
      return waitForAppToFinish();
    }
    await cleanupAtFinish();
    return 0;
  }

170
  @override
171
  Future<void> cleanupAfterSignal() async {
172
    await stopEchoingDeviceLog();
173 174 175
    if (_didAttach) {
      appFinished();
    }
176
    await exitApp();
177 178
  }

179
  @override
180
  Future<void> cleanupAtFinish() async {
181
    for (final FlutterDevice flutterDevice in flutterDevices) {
182
      await flutterDevice.device.dispose();
183 184
    }

185
    await stopEchoingDeviceLog();
186 187
  }

188
  @override
189
  void printHelp({ @required bool details }) {
190
    globals.printStatus('Flutter run key commands.');
191 192
    if (details) {
      printHelpDetails();
193
    }
194
    commandHelp.h.print();
195
    if (_didAttach) {
196
      commandHelp.d.print();
197
    }
198
    commandHelp.c.print();
199
    commandHelp.q.print();
200 201 202 203 204 205
    for (final FlutterDevice device in flutterDevices) {
      final String dname = device.device.name;
      if (device.vmService != null) {
        // Caution: This log line is parsed by device lab tests.
        globals.printStatus(
          'An Observatory debugger and profiler on $dname is available at: '
206
          '${device.vmService.httpAddress}',
207 208
        );
      }
209
    }
210
  }
211 212

  @override
213
  Future<void> preExit() async {
214
    for (final FlutterDevice device in flutterDevices) {
215
      // If we're running in release mode, stop the app using the device logic.
216
      if (device.vmService == null) {
217
        await device.device.stopApp(device.package, userIdentifier: device.userIdentifier);
218
      }
219
    }
220
    await super.preExit();
221
  }
222
}