preview_device.dart 5.61 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// 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';
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 'cache.dart';
import 'desktop_device.dart';
import 'devfs.dart';
import 'device.dart';
import 'device_port_forwarder.dart';
import 'project.dart';
import 'protocol_discovery.dart';

typedef BundleBuilderFactory = BundleBuilder Function();

BundleBuilder _defaultBundleBuilder() {
  return BundleBuilder();
}

/// A device type that runs a prebuilt desktop binary alongside a locally compiled kernel file.
///
/// This could be used to support debug local development without plugins on machines that
/// have not completed the SDK setup. These features are not fully implemented and the
/// device is not currently discoverable.
class PreviewDevice extends Device {
  PreviewDevice({
38 39 40 41
    required Platform platform,
    required ProcessManager processManager,
    required Logger logger,
    required FileSystem fileSystem,
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
    @visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder,
  }) : _platform = platform,
       _processManager = processManager,
       _logger = logger,
       _fileSystem = fileSystem,
       _bundleBuilderFactory = builderFactory,
       super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.custom);

  final Platform _platform;
  final ProcessManager _processManager;
  final Logger _logger;
  final FileSystem _fileSystem;
  final BundleBuilderFactory _bundleBuilderFactory;

  @override
  void clearLogs() { }

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

  @override
63
  Future<String?> get emulatorId async => null;
64 65 66 67

  final DesktopLogReader _logReader = DesktopLogReader();

  @override
68
  FutureOr<DeviceLogReader> getLogReader({ApplicationPackage? app, bool includePastLogs = false}) => _logReader;
69 70

  @override
71
  Future<bool> installApp(ApplicationPackage? app, {String? userIdentifier}) async => true;
72 73

  @override
74
  Future<bool> isAppInstalled(ApplicationPackage app, {String? userIdentifier}) async => false;
75 76

  @override
77
  Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false;
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

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

  @override
  bool isSupported() => true;

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

  @override
  String get name => 'preview';

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

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

97
  Process? _process;
98 99

  @override
100
  Future<LaunchResult> startApp(ApplicationPackage? package, {
101 102 103 104
    String? mainPath,
    String? route,
    required DebuggingOptions debuggingOptions,
    Map<String, dynamic> platformArgs = const <String, dynamic>{},
105 106
    bool prebuiltApplication = false,
    bool ipv6 = false,
107
    String? userIdentifier,
108
  }) async {
109
    final Directory assetDirectory = _fileSystem.systemTempDirectory
110 111 112
      .createTempSync('flutter_preview.');

    // Build assets and perform initial compilation.
113
    Status? status;
114 115 116 117 118 119 120 121 122 123
    try {
      status = _logger.startProgress('Compiling application for preview...');
      await _bundleBuilderFactory().build(
        buildInfo: debuggingOptions.buildInfo,
        mainPath: mainPath,
        platform: TargetPlatform.tester,
        assetDirPath: getAssetBuildDirectory(),
      );
      copyDirectory(_fileSystem.directory(
        getAssetBuildDirectory()),
124
        assetDirectory.childDirectory('data').childDirectory('flutter_assets'),
125 126
      );
    } finally {
127
      status?.stop();
128 129 130
    }

    // Merge with precompiled executable.
131 132
    final Directory precompiledDirectory = _fileSystem.directory(_fileSystem.path.join(Cache.flutterRoot!, 'artifacts_temp', 'Debug'));
    copyDirectory(precompiledDirectory, assetDirectory);
133 134 135

    final Process process = await _processManager.start(
      <String>[
136
        assetDirectory.childFile('splash').path,
137 138 139 140 141 142
      ],
    );
    _process = process;
    _logReader.initializeProcess(process);

    final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(_logReader,
143 144
      devicePort: debuggingOptions.deviceVmServicePort,
      hostPort: debuggingOptions.hostVmServicePort,
145 146 147 148
      ipv6: ipv6,
      logger: _logger,
    );
    try {
149
      final Uri? observatoryUri = await observatoryDiscovery.uri;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
      if (observatoryUri != null) {
        return LaunchResult.succeeded(observatoryUri: observatoryUri);
      }
      _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 {
      await observatoryDiscovery.cancel();
    }
    return LaunchResult.failed();
  }

  @override
166
  Future<bool> stopApp(ApplicationPackage? app, {String? userIdentifier}) async {
167
    return _process?.kill() ?? false;
168 169 170 171 172 173 174 175 176 177 178
  }

  @override
  Future<TargetPlatform> get targetPlatform async {
    if (_platform.isWindows) {
      return TargetPlatform.windows_x64;
    }
    return TargetPlatform.tester;
  }

  @override
179
  Future<bool> uninstallApp(ApplicationPackage app, {String? userIdentifier}) async {
180 181 182 183
    return true;
  }

  @override
184
  DevFSWriter createDevFSWriter(ApplicationPackage? app, String? userIdentifier) {
185 186 187
    return LocalDevFSWriter(fileSystem: _fileSystem);
  }
}