| // 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 'artifacts.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 'desktop_device.dart'; |
| import 'devfs.dart'; |
| import 'device.dart'; |
| import 'device_port_forwarder.dart'; |
| import 'features.dart'; |
| import 'project.dart'; |
| import 'protocol_discovery.dart'; |
| |
| typedef BundleBuilderFactory = BundleBuilder Function(); |
| |
| BundleBuilder _defaultBundleBuilder() { |
| return BundleBuilder(); |
| } |
| |
| class PreviewDeviceDiscovery extends DeviceDiscovery { |
| 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, |
| _features = featureFlags; |
| |
| 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 |
| Future<List<Device>> devices({ |
| Duration? timeout, |
| DeviceDiscoveryFilter? filter, |
| }) 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, |
| ); |
| final bool matchesRequirements; |
| if (!_features.isPreviewDeviceEnabled) { |
| matchesRequirements = false; |
| } else if (filter == null) { |
| matchesRequirements = true; |
| } else { |
| matchesRequirements = await filter.matchesRequirements(device); |
| } |
| return <Device>[ |
| if (matchesRequirements) |
| device, |
| ]; |
| } |
| |
| @override |
| Future<List<Device>> discoverDevices({ |
| Duration? timeout, |
| DeviceDiscoveryFilter? filter, |
| }) { |
| return devices(); |
| } |
| } |
| |
| /// A device type that runs a prebuilt desktop binary alongside a locally compiled kernel file. |
| class PreviewDevice extends Device { |
| PreviewDevice({ |
| required ProcessManager processManager, |
| required Logger logger, |
| required FileSystem fileSystem, |
| required Artifacts artifacts, |
| required File previewBinary, |
| @visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder, |
| }) : _previewBinary = previewBinary, |
| _processManager = processManager, |
| _logger = logger, |
| _fileSystem = fileSystem, |
| _bundleBuilderFactory = builderFactory, |
| _artifacts = artifacts, |
| super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.custom); |
| |
| final ProcessManager _processManager; |
| final Logger _logger; |
| final FileSystem _fileSystem; |
| final BundleBuilderFactory _bundleBuilderFactory; |
| final Artifacts _artifacts; |
| final File _previewBinary; |
| |
| /// The set of plugins that are allowed to be used by Preview users. |
| /// |
| /// Currently no plugins are supported. |
| static const List<String> supportedPubPlugins = <String>[]; |
| |
| @override |
| void clearLogs() { } |
| |
| @override |
| Future<void> dispose() async { } |
| |
| @override |
| Future<String?> get emulatorId async => null; |
| |
| final DesktopLogReader _logReader = DesktopLogReader(); |
| |
| @override |
| FutureOr<DeviceLogReader> getLogReader({ApplicationPackage? app, bool includePastLogs = false}) => _logReader; |
| |
| @override |
| Future<bool> installApp(ApplicationPackage? app, {String? userIdentifier}) async => true; |
| |
| @override |
| Future<bool> isAppInstalled(ApplicationPackage app, {String? userIdentifier}) async => false; |
| |
| @override |
| Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false; |
| |
| @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'; |
| |
| Process? _process; |
| |
| @override |
| Future<LaunchResult> startApp(ApplicationPackage? package, { |
| String? mainPath, |
| String? route, |
| required DebuggingOptions debuggingOptions, |
| Map<String, dynamic> platformArgs = const <String, dynamic>{}, |
| bool prebuiltApplication = false, |
| bool ipv6 = false, |
| String? userIdentifier, |
| }) async { |
| final Directory assetDirectory = _fileSystem.systemTempDirectory |
| .createTempSync('flutter_preview.'); |
| |
| // Build assets and perform initial compilation. |
| Status? status; |
| try { |
| status = _logger.startProgress('Compiling application for preview...'); |
| await _bundleBuilderFactory().build( |
| buildInfo: debuggingOptions.buildInfo, |
| mainPath: mainPath, |
| platform: TargetPlatform.windows_x64, |
| assetDirPath: getAssetBuildDirectory(), |
| ); |
| copyDirectory(_fileSystem.directory( |
| getAssetBuildDirectory()), |
| assetDirectory.childDirectory('data').childDirectory('flutter_assets'), |
| ); |
| } finally { |
| status?.stop(); |
| } |
| |
| // Merge with precompiled executable. |
| 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); |
| |
| final Process process = await _processManager.start( |
| <String>[copiedPreviewBinaryPath], |
| ); |
| _process = process; |
| _logReader.initializeProcess(process); |
| |
| final ProtocolDiscovery vmServiceDiscovery = ProtocolDiscovery.vmService(_logReader, |
| devicePort: debuggingOptions.deviceVmServicePort, |
| hostPort: debuggingOptions.hostVmServicePort, |
| ipv6: ipv6, |
| logger: _logger, |
| ); |
| try { |
| final Uri? vmServiceUri = await vmServiceDiscovery.uri; |
| if (vmServiceUri != null) { |
| return LaunchResult.succeeded(vmServiceUri: vmServiceUri); |
| } |
| _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 vmServiceDiscovery.cancel(); |
| } |
| return LaunchResult.failed(); |
| } |
| |
| @override |
| Future<bool> stopApp(ApplicationPackage? app, {String? userIdentifier}) async { |
| return _process?.kill() ?? false; |
| } |
| |
| @override |
| Future<TargetPlatform> get targetPlatform async { |
| return TargetPlatform.windows_x64; |
| } |
| |
| @override |
| Future<bool> uninstallApp(ApplicationPackage app, {String? userIdentifier}) async { |
| return true; |
| } |
| |
| @override |
| DevFSWriter createDevFSWriter(ApplicationPackage? app, String? userIdentifier) { |
| return LocalDevFSWriter(fileSystem: _fileSystem); |
| } |
| } |