preview_device.dart 7.67 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2014 The Flutter 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';

import 'package:meta/meta.dart';
import 'package:process/process.dart';

import 'application_package.dart';
11
import 'artifacts.dart';
12 13 14 15 16 17 18 19 20 21
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/platform.dart';
import 'build_info.dart';
import 'bundle_builder.dart';
import 'desktop_device.dart';
import 'devfs.dart';
import 'device.dart';
import 'device_port_forwarder.dart';
22
import 'features.dart';
23 24 25 26 27 28 29 30 31
import 'project.dart';
import 'protocol_discovery.dart';

typedef BundleBuilderFactory = BundleBuilder Function();

BundleBuilder _defaultBundleBuilder() {
  return BundleBuilder();
}

32
class PreviewDeviceDiscovery extends PollingDeviceDiscovery {
33 34 35 36 37 38 39 40 41 42 43 44
  PreviewDeviceDiscovery({
    required Platform platform,
    required Artifacts artifacts,
    required FileSystem fileSystem,
    required Logger logger,
    required ProcessManager processManager,
    required FeatureFlags featureFlags,
  }) : _artifacts = artifacts,
       _logger = logger,
       _processManager = processManager,
       _fileSystem = fileSystem,
       _platform = platform,
45 46
       _features = featureFlags,
       super('Flutter preview device');
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

  final Platform _platform;
  final Artifacts _artifacts;
  final Logger _logger;
  final ProcessManager _processManager;
  final FileSystem _fileSystem;
  final FeatureFlags _features;

  @override
  bool get canListAnything => _platform.isWindows;

  @override
  bool get supportsPlatform => _platform.isWindows;

  @override
  List<String> get wellKnownIds => <String>['preview'];

  @override
65
  Future<List<Device>> pollingGetDevices({
66 67 68 69 70 71 72 73 74 75 76 77 78 79
    Duration? timeout,
  }) async {
    final File previewBinary = _fileSystem.file(_artifacts.getArtifactPath(Artifact.flutterPreviewDevice));
    if (!previewBinary.existsSync()) {
      return const <Device>[];
    }
    final PreviewDevice device = PreviewDevice(
      artifacts: _artifacts,
      fileSystem: _fileSystem,
      logger: _logger,
      processManager: _processManager,
      previewBinary: previewBinary,
    );
    return <Device>[
80
      if (_features.isPreviewDeviceEnabled)
81 82 83 84 85 86 87 88 89 90 91 92 93
        device,
    ];
  }

  @override
  Future<List<Device>> discoverDevices({
    Duration? timeout,
    DeviceDiscoveryFilter? filter,
  }) {
    return devices();
  }
}

94 95 96
/// A device type that runs a prebuilt desktop binary alongside a locally compiled kernel file.
class PreviewDevice extends Device {
  PreviewDevice({
97 98 99
    required ProcessManager processManager,
    required Logger logger,
    required FileSystem fileSystem,
100 101
    required Artifacts artifacts,
    required File previewBinary,
102
    @visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder,
103
  }) : _previewBinary = previewBinary,
104 105 106 107
       _processManager = processManager,
       _logger = logger,
       _fileSystem = fileSystem,
       _bundleBuilderFactory = builderFactory,
108
       _artifacts = artifacts,
109
       super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.windowsPreview);
110 111 112 113 114

  final ProcessManager _processManager;
  final Logger _logger;
  final FileSystem _fileSystem;
  final BundleBuilderFactory _bundleBuilderFactory;
115 116
  final Artifacts _artifacts;
  final File _previewBinary;
117

118 119 120 121 122
  /// The set of plugins that are allowed to be used by Preview users.
  ///
  /// Currently no plugins are supported.
  static const List<String> supportedPubPlugins = <String>[];

123 124 125 126 127 128 129
  @override
  void clearLogs() { }

  @override
  Future<void> dispose() async { }

  @override
130
  Future<String?> get emulatorId async => null;
131 132 133 134

  final DesktopLogReader _logReader = DesktopLogReader();

  @override
135
  FutureOr<DeviceLogReader> getLogReader({ApplicationPackage? app, bool includePastLogs = false}) => _logReader;
136 137

  @override
138
  Future<bool> installApp(ApplicationPackage? app, {String? userIdentifier}) async => true;
139 140

  @override
141
  Future<bool> isAppInstalled(ApplicationPackage app, {String? userIdentifier}) async => false;
142 143

  @override
144
  Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false;
145 146 147 148 149 150 151 152 153 154 155

  @override
  Future<bool> get isLocalEmulator async => false;

  @override
  bool isSupported() => true;

  @override
  bool isSupportedForProject(FlutterProject flutterProject) => true;

  @override
156
  String get name => 'Preview';
157 158 159 160 161 162 163

  @override
  DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();

  @override
  Future<String> get sdkNameAndVersion async => 'preview';

164
  Process? _process;
165 166

  @override
167
  Future<LaunchResult> startApp(ApplicationPackage? package, {
168 169 170 171
    String? mainPath,
    String? route,
    required DebuggingOptions debuggingOptions,
    Map<String, dynamic> platformArgs = const <String, dynamic>{},
172 173
    bool prebuiltApplication = false,
    bool ipv6 = false,
174
    String? userIdentifier,
175
  }) async {
176
    final Directory assetDirectory = _fileSystem.systemTempDirectory
177 178 179
      .createTempSync('flutter_preview.');

    // Build assets and perform initial compilation.
180
    Status? status;
181 182 183 184 185
    try {
      status = _logger.startProgress('Compiling application for preview...');
      await _bundleBuilderFactory().build(
        buildInfo: debuggingOptions.buildInfo,
        mainPath: mainPath,
186
        platform: TargetPlatform.windows_x64,
187 188 189 190
        assetDirPath: getAssetBuildDirectory(),
      );
      copyDirectory(_fileSystem.directory(
        getAssetBuildDirectory()),
191
        assetDirectory.childDirectory('data').childDirectory('flutter_assets'),
192 193
      );
    } finally {
194
      status?.stop();
195 196 197
    }

    // Merge with precompiled executable.
198 199 200 201 202 203 204 205 206
    final String copiedPreviewBinaryPath = assetDirectory.childFile(_previewBinary.basename).path;
    _previewBinary.copySync(copiedPreviewBinaryPath);

    final String windowsPath = _artifacts
      .getArtifactPath(Artifact.windowsDesktopPath, platform: TargetPlatform.windows_x64, mode: BuildMode.debug);
    final File windowsDll = _fileSystem.file(_fileSystem.path.join(windowsPath, 'flutter_windows.dll'));
    final File icu = _fileSystem.file(_fileSystem.path.join(windowsPath, 'icudtl.dat'));
    windowsDll.copySync(assetDirectory.childFile('flutter_windows.dll').path);
    icu.copySync(assetDirectory.childDirectory('data').childFile('icudtl.dat').path);
207 208

    final Process process = await _processManager.start(
209
      <String>[copiedPreviewBinaryPath],
210 211 212 213
    );
    _process = process;
    _logReader.initializeProcess(process);

214
    final ProtocolDiscovery vmServiceDiscovery = ProtocolDiscovery.vmService(_logReader,
215 216
      devicePort: debuggingOptions.deviceVmServicePort,
      hostPort: debuggingOptions.hostVmServicePort,
217 218 219 220
      ipv6: ipv6,
      logger: _logger,
    );
    try {
221 222 223
      final Uri? vmServiceUri = await vmServiceDiscovery.uri;
      if (vmServiceUri != null) {
        return LaunchResult.succeeded(vmServiceUri: vmServiceUri);
224 225 226 227 228 229 230 231
      }
      _logger.printError(
        'Error waiting for a debug connection: '
        'The log reader stopped unexpectedly.',
      );
    } on Exception catch (error) {
      _logger.printError('Error waiting for a debug connection: $error');
    } finally {
232
      await vmServiceDiscovery.cancel();
233 234 235 236 237
    }
    return LaunchResult.failed();
  }

  @override
238
  Future<bool> stopApp(ApplicationPackage? app, {String? userIdentifier}) async {
239
    return _process?.kill() ?? false;
240 241 242 243
  }

  @override
  Future<TargetPlatform> get targetPlatform async {
244
    return TargetPlatform.windows_x64;
245 246 247
  }

  @override
248
  Future<bool> uninstallApp(ApplicationPackage app, {String? userIdentifier}) async {
249 250 251 252
    return true;
  }

  @override
253
  DevFSWriter createDevFSWriter(ApplicationPackage? app, String? userIdentifier) {
254 255 256
    return LocalDevFSWriter(fileSystem: _fileSystem);
  }
}