1// Copyright 2018 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import '../application_package.dart'; 6import '../base/io.dart'; 7import '../base/os.dart'; 8import '../base/platform.dart'; 9import '../base/process_manager.dart'; 10import '../build_info.dart'; 11import '../cache.dart'; 12import '../desktop.dart'; 13import '../device.dart'; 14import '../globals.dart'; 15import '../macos/application_package.dart'; 16import '../project.dart'; 17import '../protocol_discovery.dart'; 18import 'build_macos.dart'; 19import 'macos_workflow.dart'; 20 21/// A device that represents a desktop MacOS target. 22class MacOSDevice extends Device { 23 MacOSDevice() : super( 24 'macOS', 25 category: Category.desktop, 26 platformType: PlatformType.macos, 27 ephemeral: false, 28 ); 29 30 @override 31 void clearLogs() { } 32 33 @override 34 DeviceLogReader getLogReader({ ApplicationPackage app }) { 35 return _deviceLogReader; 36 } 37 final DesktopLogReader _deviceLogReader = DesktopLogReader(); 38 39 // Since the host and target devices are the same, no work needs to be done 40 // to install the application. 41 @override 42 Future<bool> installApp(ApplicationPackage app) async => true; 43 44 // Since the host and target devices are the same, no work needs to be done 45 // to install the application. 46 @override 47 Future<bool> isAppInstalled(ApplicationPackage app) async => true; 48 49 // Since the host and target devices are the same, no work needs to be done 50 // to install the application. 51 @override 52 Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true; 53 54 @override 55 Future<bool> get isLocalEmulator async => false; 56 57 @override 58 Future<String> get emulatorId async => null; 59 60 @override 61 bool isSupported() => true; 62 63 @override 64 String get name => 'macOS'; 65 66 @override 67 DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder(); 68 69 @override 70 Future<String> get sdkNameAndVersion async => os.name; 71 72 @override 73 Future<LaunchResult> startApp( 74 covariant MacOSApp package, { 75 String mainPath, 76 String route, 77 DebuggingOptions debuggingOptions, 78 Map<String, dynamic> platformArgs, 79 bool prebuiltApplication = false, 80 bool usesTerminalUi = true, 81 bool ipv6 = false, 82 }) async { 83 // Stop any running applications with the same executable. 84 if (!prebuiltApplication) { 85 Cache.releaseLockEarly(); 86 await buildMacOS( 87 flutterProject: FlutterProject.current(), 88 buildInfo: debuggingOptions?.buildInfo, 89 targetOverride: mainPath, 90 ); 91 } 92 93 // Ensure that the executable is locatable. 94 final String executable = package.executable(debuggingOptions?.buildInfo?.mode); 95 if (executable == null) { 96 printError('Unable to find executable to run'); 97 return LaunchResult.failed(); 98 } 99 100 // Make sure to call stop app after we've built. 101 await stopApp(package); 102 final Process process = await processManager.start(<String>[ 103 executable 104 ]); 105 if (debuggingOptions?.buildInfo?.isRelease == true) { 106 return LaunchResult.succeeded(); 107 } 108 _deviceLogReader.initializeProcess(process); 109 final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(_deviceLogReader); 110 try { 111 final Uri observatoryUri = await observatoryDiscovery.uri; 112 // Bring app to foreground. 113 await processManager.run(<String>[ 114 'open', package.applicationBundle(debuggingOptions?.buildInfo?.mode), 115 ]); 116 return LaunchResult.succeeded(observatoryUri: observatoryUri); 117 } catch (error) { 118 printError('Error waiting for a debug connection: $error'); 119 return LaunchResult.failed(); 120 } finally { 121 await observatoryDiscovery.cancel(); 122 } 123 } 124 125 // TODO(jonahwilliams): implement using process manager. 126 // currently we rely on killing the isolate taking down the application. 127 @override 128 Future<bool> stopApp(covariant MacOSApp app) async { 129 return killProcess(app.executable(BuildMode.debug)); 130 } 131 132 @override 133 Future<TargetPlatform> get targetPlatform async => TargetPlatform.darwin_x64; 134 135 // Since the host and target devices are the same, no work needs to be done 136 // to uninstall the application. 137 @override 138 Future<bool> uninstallApp(ApplicationPackage app) async => true; 139 140 @override 141 bool isSupportedForProject(FlutterProject flutterProject) { 142 return flutterProject.macos.existsSync(); 143 } 144} 145 146class MacOSDevices extends PollingDeviceDiscovery { 147 MacOSDevices() : super('macOS devices'); 148 149 @override 150 bool get supportsPlatform => platform.isMacOS; 151 152 @override 153 bool get canListAnything => macOSWorkflow.canListDevices; 154 155 @override 156 Future<List<Device>> pollingGetDevices() async { 157 if (!canListAnything) { 158 return const <Device>[]; 159 } 160 return <Device>[ 161 MacOSDevice(), 162 ]; 163 } 164 165 @override 166 Future<List<String>> getDiagnostics() async => const <String>[]; 167} 168