Add Designed by iPad attach destination for ARM macOS (#84411)

This commit is contained in:
Jenn Magder 2021-06-14 12:09:02 -07:00 committed by GitHub
parent f4f1c2103e
commit e87a85c30e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 316 additions and 1 deletions

View file

@ -24,6 +24,7 @@ import '../fuchsia/fuchsia_device.dart';
import '../globals_null_migrated.dart' as globals;
import '../ios/devices.dart';
import '../ios/simulators.dart';
import '../macos/macos_ipad_device.dart';
import '../mdns_discovery.dart';
import '../project.dart';
import '../protocol_discovery.dart';
@ -176,6 +177,9 @@ known, it can be explicitly provided to attach via the command-line, e.g.
@override
Future<void> validateCommand() async {
// ARM macOS as an iOS target is hidden, except for attach.
MacOSDesignedForIPadDevices.allowDiscovery = true;
await super.validateCommand();
if (await findTargetDevice() == null) {
throwToolExit(null);
@ -262,7 +266,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
}
rethrow;
}
} else if ((device is IOSDevice) || (device is IOSSimulator)) {
} else if ((device is IOSDevice) || (device is IOSSimulator) || (device is MacOSDesignedForIPadDevice)) {
final Uri uriFromMdns =
await MDnsObservatoryDiscovery.instance.getObservatoryUri(
appId,

View file

@ -30,6 +30,7 @@ import 'ios/ios_workflow.dart';
import 'ios/simulators.dart';
import 'linux/linux_device.dart';
import 'macos/macos_device.dart';
import 'macos/macos_ipad_device.dart';
import 'macos/macos_workflow.dart';
import 'macos/xcdevice.dart';
import 'tester/flutter_tester.dart';
@ -105,6 +106,14 @@ class FlutterDeviceManager extends DeviceManager {
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
),
MacOSDesignedForIPadDevices(
processManager: processManager,
iosWorkflow: iosWorkflow,
logger: logger,
platform: platform,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
),
LinuxDevices(
platform: platform,
featureFlags: featureFlags,

View file

@ -0,0 +1,147 @@
// 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.
// @dart = 2.8
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../application_package.dart';
import '../base/file_system.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 '../ios/application_package.dart';
import '../ios/ios_workflow.dart';
import '../project.dart';
/// Represents an ARM macOS target that can run iPad apps.
///
/// https://developer.apple.com/documentation/apple-silicon/running-your-ios-apps-on-macos
class MacOSDesignedForIPadDevice extends DesktopDevice {
MacOSDesignedForIPadDevice({
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : _operatingSystemUtils = operatingSystemUtils,
super(
'designed-for-ipad',
platformType: PlatformType.macos,
ephemeral: false,
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
);
final OperatingSystemUtils _operatingSystemUtils;
@override
String get name => 'Mac Designed for iPad';
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.darwin;
@override
bool isSupported() => _operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm;
@override
bool isSupportedForProject(FlutterProject flutterProject) {
return flutterProject.ios.existsSync() && _operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm;
}
@override
String executablePathForDevice(ApplicationPackage package, BuildMode buildMode) => null;
@override
Future<LaunchResult> startApp(
IOSApp package, {
String mainPath,
String route,
@required DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs = const <String, dynamic>{},
bool prebuiltApplication = false,
bool ipv6 = false,
String userIdentifier,
}) async {
// Only attaching to a running app launched from Xcode is supported.
throw UnimplementedError('Building for "$name" is not supported.');
}
@override
Future<bool> stopApp(
IOSApp app, {
String userIdentifier,
}) async => false;
@override
Future<void> buildForDevice(
covariant IOSApp package, {
String mainPath,
BuildInfo buildInfo,
}) async {
// Only attaching to a running app launched from Xcode is supported.
throw UnimplementedError('Building for "$name" is not supported.');
}
}
class MacOSDesignedForIPadDevices extends PollingDeviceDiscovery {
MacOSDesignedForIPadDevices({
@required Platform platform,
@required IOSWorkflow iosWorkflow,
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : _logger = logger,
_platform = platform,
_iosWorkflow = iosWorkflow,
_processManager = processManager,
_fileSystem = fileSystem,
_operatingSystemUtils = operatingSystemUtils,
super('Mac designed for iPad devices');
final IOSWorkflow _iosWorkflow;
final Platform _platform;
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fileSystem;
final OperatingSystemUtils _operatingSystemUtils;
@override
bool get supportsPlatform => _platform.isMacOS;
/// iOS (not desktop macOS) development is enabled, the host is an ARM Mac,
/// and discovery is allowed for this command.
@override
bool get canListAnything =>
_iosWorkflow.canListDevices && _operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm && allowDiscovery;
/// Set to show ARM macOS as an iOS device target.
static bool allowDiscovery = false;
@override
Future<List<Device>> pollingGetDevices({Duration timeout}) async {
if (!canListAnything) {
return const <Device>[];
}
return <Device>[
MacOSDesignedForIPadDevice(
processManager: _processManager,
logger: _logger,
fileSystem: _fileSystem,
operatingSystemUtils: _operatingSystemUtils,
),
];
}
@override
Future<List<String>> getDiagnostics() async => const <String>[];
}

View file

@ -24,6 +24,7 @@ import 'package:flutter_tools/src/device_port_forwarder.dart';
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
import 'package:flutter_tools/src/ios/application_package.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
@ -58,6 +59,10 @@ final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate(
);
void main() {
tearDown(() {
MacOSDesignedForIPadDevices.allowDiscovery = false;
});
group('attach', () {
StreamLogger logger;
FileSystem testFileSystem;
@ -410,6 +415,7 @@ void main() {
expect(testLogger.statusText, containsIgnoringWhitespace('More than one device'));
expect(testLogger.statusText, contains('xx1'));
expect(testLogger.statusText, contains('yy2'));
expect(MacOSDesignedForIPadDevices.allowDiscovery, isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),

View file

@ -0,0 +1,149 @@
// 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.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/desktop_device.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/ios_workflow.dart';
import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
void main() {
group('MacOSDesignedForIPadDevices', () {
tearDown(() {
MacOSDesignedForIPadDevices.allowDiscovery = false;
});
testWithoutContext('does not support non-macOS plaforms', () async {
MacOSDesignedForIPadDevices.allowDiscovery = true;
final MacOSDesignedForIPadDevices discoverer = MacOSDesignedForIPadDevices(
platform: FakePlatform(operatingSystem: 'windows'),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_arm),
iosWorkflow: FakeIOSWorkflow(canListDevices: true),
);
expect(discoverer.supportsPlatform, isFalse);
});
testWithoutContext('discovery not allowed', () async {
final MacOSDesignedForIPadDevices discoverer = MacOSDesignedForIPadDevices(
platform: FakePlatform(operatingSystem: 'macos'),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_arm),
iosWorkflow: FakeIOSWorkflow(canListDevices: true),
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
expect(devices, isEmpty);
});
testWithoutContext('no device on x86', () async {
MacOSDesignedForIPadDevices.allowDiscovery = true;
final MacOSDesignedForIPadDevices discoverer = MacOSDesignedForIPadDevices(
platform: FakePlatform(operatingSystem: 'macos'),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_x64),
iosWorkflow: FakeIOSWorkflow(canListDevices: true),
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
expect(devices, isEmpty);
});
testWithoutContext('no device on when iOS development off', () async {
MacOSDesignedForIPadDevices.allowDiscovery = true;
final MacOSDesignedForIPadDevices discoverer = MacOSDesignedForIPadDevices(
platform: FakePlatform(operatingSystem: 'macos'),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_arm),
iosWorkflow: FakeIOSWorkflow(canListDevices: false),
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
expect(devices, isEmpty);
});
testWithoutContext('device discovery on arm', () async {
MacOSDesignedForIPadDevices.allowDiscovery = true;
final MacOSDesignedForIPadDevices discoverer = MacOSDesignedForIPadDevices(
platform: FakePlatform(operatingSystem: 'macos'),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_arm),
iosWorkflow: FakeIOSWorkflow(canListDevices: true),
);
expect(discoverer.supportsPlatform, isTrue);
List<Device> devices = await discoverer.devices;
expect(devices, hasLength(1));
final Device device = devices.single;
expect(device, isA<MacOSDesignedForIPadDevice>());
expect(device.id, 'designed-for-ipad');
// Timeout ignored.
devices = await discoverer.discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
});
});
testWithoutContext('MacOSDesignedForIPadDevice properties', () async {
final MacOSDesignedForIPadDevice device = MacOSDesignedForIPadDevice(
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.darwin_arm),
);
expect(device.id, 'designed-for-ipad');
expect(await device.isLocalEmulator, isFalse);
expect(device.name, 'Mac Designed for iPad');
expect(device.portForwarder, isNot(isNull));
expect(await device.targetPlatform, TargetPlatform.darwin);
expect(await device.installApp(null), isTrue);
expect(await device.isAppInstalled(null), isTrue);
expect(await device.isLatestBuildInstalled(null), isTrue);
expect(await device.uninstallApp(null), isTrue);
expect(device.isSupported(), isTrue);
expect(device.getLogReader(), isA<DesktopLogReader>());
expect(await device.stopApp(null), isFalse);
await expectLater(() => device.startApp(null, debuggingOptions: null), throwsA(isA<UnimplementedError>()));
await expectLater(() => device.buildForDevice(null), throwsA(isA<UnimplementedError>()));
expect(device.executablePathForDevice(null, null), null);
});
}
class FakeIOSWorkflow extends Fake implements IOSWorkflow {
FakeIOSWorkflow({@required this.canListDevices});
@override
final bool canListDevices;
}