run_cold.dart 6.16 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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 11
import 'device.dart';
import 'globals.dart';
12
import 'resident_runner.dart';
13
import 'tracing.dart';
14
import 'vmservice.dart';
15

16
// TODO(mklim): Test this, flutter/flutter#23031.
17 18
class ColdRunner extends ResidentRunner {
  ColdRunner(
19
    List<FlutterDevice> devices, {
20 21
    String target,
    DebuggingOptions debuggingOptions,
22
    this.traceStartup = false,
23
    this.awaitFirstFrameWhenTracing = true,
24
    this.applicationBinary,
25
    bool ipv6 = false,
26
    bool stayResident = true,
27
  }) : super(devices,
28 29
             target: target,
             debuggingOptions: debuggingOptions,
30
             hotMode: false,
31 32
             stayResident: stayResident,
             ipv6: ipv6);
33

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

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

  @override
  bool get canHotRestart => false;

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

63 64 65 66 67
    for (FlutterDevice device in flutterDevices) {
      final int result = await device.runCold(
        coldRunner: this,
        route: route,
      );
68
      if (result != 0) {
69
        return result;
70
      }
71 72
    }

73
    // Connect to observatory.
74 75 76 77 78 79 80 81
    if (debuggingOptions.debuggingEnabled) {
      try {
        await connectToServiceProtocol();
      } on String catch (message) {
        printError(message);
        return 2;
      }
    }
82

83 84
    if (flutterDevices.first.observatoryUris != null) {
      // For now, only support one debugger connection.
85
      connectionInfoCompleter?.complete(DebugConnectionInfo(
86 87
        httpUri: flutterDevices.first.observatoryUris.first,
        wsUri: flutterDevices.first.vmServices.first.wsAddress,
88 89
      ));
    }
90

91
    printTrace('Application running.');
92

93
    for (FlutterDevice device in flutterDevices) {
94
      if (device.vmServices == null) {
95
        continue;
96
      }
97 98 99
      device.initLogReader();
      await device.refreshViews();
      printTrace('Connected to ${device.device.name}');
100
    }
101

102 103 104 105
    if (traceStartup) {
      // Only trace startup for the first device.
      final FlutterDevice device = flutterDevices.first;
      if (device.vmServices != null && device.vmServices.isNotEmpty) {
106 107 108 109 110
        printStatus('Tracing startup on ${device.device.name}.');
        await downloadStartupTrace(
          device.vmServices.first,
          awaitFirstFrame: awaitFirstFrameWhenTracing,
        );
111
      }
112
      appFinished();
113 114
    }

115 116
    appStartedCompleter?.complete();

117
    if (stayResident && !traceStartup) {
118
      return waitForAppToFinish();
119
    }
120 121
    await cleanupAtFinish();
    return 0;
122 123
  }

124 125 126 127 128 129 130 131 132 133
  @override
  Future<int> attach({
    Completer<DebugConnectionInfo> connectionInfoCompleter,
    Completer<void> appStartedCompleter,
  }) async {
    _didAttach = true;
    try {
      await connectToServiceProtocol();
    } catch (error) {
      printError('Error connecting to the service protocol: $error');
134 135 136 137 138 139 140
      // https://github.com/flutter/flutter/issues/33050
      // TODO(blasten): Remove this check once https://issuetracker.google.com/issues/132325318 has been fixed.
      if (await hasDeviceRunningAndroidQ(flutterDevices) &&
          error.toString().contains(kAndroidQHttpConnectionClosedExp)) {
        printStatus('🔨 If you are using an emulator running Android Q Beta, consider using an emulator running API level 29 or lower.');
        printStatus('Learn more about the status of this issue on https://issuetracker.google.com/issues/132325318');
      }
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
      return 2;
    }
    for (FlutterDevice device in flutterDevices) {
      device.initLogReader();
    }
    await refreshViews();
    for (FlutterDevice device in flutterDevices) {
      for (FlutterView view in device.views) {
        printTrace('Connected to $view.');
      }
    }
    appStartedCompleter?.complete();
    if (stayResident) {
      return waitForAppToFinish();
    }
    await cleanupAtFinish();
    return 0;
  }

160
  @override
161
  Future<void> cleanupAfterSignal() async {
162
    await stopEchoingDeviceLog();
163 164 165
    if (_didAttach) {
      appFinished();
    }
166
    await exitApp();
167 168
  }

169
  @override
170
  Future<void> cleanupAtFinish() async {
171
    await stopEchoingDeviceLog();
172 173
  }

174
  @override
175
  void printHelp({ @required bool details }) {
176
    bool haveDetails = false;
177
    bool haveAnything = false;
178 179 180
    for (FlutterDevice device in flutterDevices) {
      final String dname = device.device.name;
      if (device.observatoryUris != null) {
181
        for (Uri uri in device.observatoryUris) {
182
          printStatus('An Observatory debugger and profiler on $dname is available at $uri');
183 184
          haveAnything = true;
        }
185 186
      }
    }
187 188
    if (supportsServiceProtocol) {
      haveDetails = true;
189
      if (details) {
190
        printHelpDetails();
191 192
        haveAnything = true;
      }
193
    }
194 195 196
    final String quitMessage = _didAttach
      ? 'To detach, press "d"; to quit, press "q".'
      : 'To quit, press "q".';
197
    if (haveDetails && !details) {
198
      printStatus('For a more detailed help message, press "h". $quitMessage');
199
    } else if (haveAnything) {
200
      printStatus('To repeat this help message, press "h". $quitMessage');
201
    } else {
202
      printStatus(quitMessage);
203
    }
204
  }
205 206

  @override
207
  Future<void> preExit() async {
208 209
    for (FlutterDevice device in flutterDevices) {
      // If we're running in release mode, stop the app using the device logic.
210
      if (device.vmServices == null || device.vmServices.isEmpty) {
211
        await device.device.stopApp(device.package);
212
      }
213
    }
214
    await super.preExit();
215
  }
216
}