// 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 'package:process/process.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; import '../base/os.dart'; import '../base/platform.dart'; import '../build_info.dart'; import '../desktop_device.dart'; import '../device.dart'; import '../macos/application_package.dart'; import '../project.dart'; import 'build_macos.dart'; import 'macos_workflow.dart'; /// A device that represents a desktop MacOS target. class MacOSDevice extends DesktopDevice { MacOSDevice({ required ProcessManager processManager, required Logger logger, required FileSystem fileSystem, required OperatingSystemUtils operatingSystemUtils, }) : _processManager = processManager, _logger = logger, _operatingSystemUtils = operatingSystemUtils, super( 'macos', platformType: PlatformType.macos, ephemeral: false, processManager: processManager, logger: logger, fileSystem: fileSystem, operatingSystemUtils: operatingSystemUtils, ); final ProcessManager _processManager; final Logger _logger; final OperatingSystemUtils _operatingSystemUtils; @override bool isSupported() => true; @override String get name => 'macOS'; @override Future<TargetPlatform> get targetPlatform async => TargetPlatform.darwin; @override Future<String> get targetPlatformDisplayName async { if (_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm64) { return 'darwin-arm64'; } return 'darwin-x64'; } @override bool isSupportedForProject(FlutterProject flutterProject) { return flutterProject.macos.existsSync(); } @override Future<void> buildForDevice({ required BuildInfo buildInfo, String? mainPath, }) async { await buildMacOS( flutterProject: FlutterProject.current(), buildInfo: buildInfo, targetOverride: mainPath, verboseLogging: _logger.isVerbose, ); } @override String? executablePathForDevice(covariant MacOSApp package, BuildInfo buildInfo) { return package.executable(buildInfo); } @override void onAttached(covariant MacOSApp package, BuildInfo buildInfo, Process process) { // Bring app to foreground. Ideally this would be done post-launch rather // than post-attach, since this won't run for release builds, but there's // no general-purpose way of knowing when a process is far enough along in // the launch process for 'open' to foreground it. final String? applicationBundle = package.applicationBundle(buildInfo); if (applicationBundle == null) { _logger.printError('Failed to foreground app; application bundle not found'); return; } _processManager.run(<String>[ 'open', applicationBundle, ]).then((ProcessResult result) { if (result.exitCode != 0) { _logger.printError('Failed to foreground app; open returned ${result.exitCode}'); } }); } } class MacOSDevices extends PollingDeviceDiscovery { MacOSDevices({ required Platform platform, required MacOSWorkflow macOSWorkflow, required ProcessManager processManager, required Logger logger, required FileSystem fileSystem, required OperatingSystemUtils operatingSystemUtils, }) : _logger = logger, _platform = platform, _macOSWorkflow = macOSWorkflow, _processManager = processManager, _fileSystem = fileSystem, _operatingSystemUtils = operatingSystemUtils, super('macOS devices'); final MacOSWorkflow _macOSWorkflow; final Platform _platform; final ProcessManager _processManager; final Logger _logger; final FileSystem _fileSystem; final OperatingSystemUtils _operatingSystemUtils; @override bool get supportsPlatform => _platform.isMacOS; @override bool get canListAnything => _macOSWorkflow.canListDevices; @override Future<List<Device>> pollingGetDevices({ Duration? timeout }) async { if (!canListAnything) { return const <Device>[]; } return <Device>[ MacOSDevice( processManager: _processManager, logger: _logger, fileSystem: _fileSystem, operatingSystemUtils: _operatingSystemUtils, ), ]; } @override Future<List<String>> getDiagnostics() async => const <String>[]; @override List<String> get wellKnownIds => const <String>['macos']; }