Commit b2909a24 authored by Chris Bracken's avatar Chris Bracken Committed by GitHub

Revert use of Xcode instruments for device lookup (#10806)

* Revert "Make device discovery asynchronous (#10803)"
This reverts commit 972be9c8.

* Revert "Use Xcode instruments to list devices (#10801)"
This reverts commit 37bb5f13.

This is to resolve a failure that looks related to a bad install of Xcode 8.0
on our build bots and should be reinstated when the infra issue is diagnosed
and resolved.

Instruments worked well when this was originally landed, and on the
following commit, but started failing two commits after this originally
landed. Manual invocation of instruments on the build host currently
results in:

```
dyld: Library not loaded: @rpath/InstrumentsAnalysisCore.framework/Versions/A/InstrumentsAnalysisCore
  Referenced from: /Applications/Xcode8.0.app/Contents/Developer/usr/bin/instruments
  Reason: image not found
Abort trap: 6
```

It appears the /Applications/Xcode8.0.app/Contents/Applications
directory (which contains Instruments) is missing on the host.
parent 972be9c8
......@@ -48,7 +48,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
bool get canListAnything => androidWorkflow.canListDevices;
@override
Future<List<Device>> pollingGetDevices() async => getAdbDevices();
List<Device> pollingGetDevices() => getAdbDevices();
}
class AndroidDevice extends Device {
......
......@@ -305,7 +305,7 @@ class AppDomain extends Domain {
final String target = _getStringArg(args, 'target');
final bool enableHotReload = _getBoolArg(args, 'hot') ?? kHotReloadDefault;
final Device device = await daemon.deviceDomain._getOrLocateDevice(deviceId);
final Device device = daemon.deviceDomain._getOrLocateDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
......@@ -493,7 +493,7 @@ class AppDomain extends Domain {
Future<List<Map<String, dynamic>>> discover(Map<String, dynamic> args) async {
final String deviceId = _getStringArg(args, 'deviceId', required: true);
final Device device = await daemon.deviceDomain._getDevice(deviceId);
final Device device = daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
......@@ -575,12 +575,11 @@ class DeviceDomain extends Domain {
final List<PollingDeviceDiscovery> _discoverers = <PollingDeviceDiscovery>[];
Future<List<Device>> getDevices([Map<String, dynamic> args]) async {
final List<Device> devices = <Device>[];
for (PollingDeviceDiscovery discoverer in _discoverers) {
devices.addAll(await discoverer.devices);
}
return devices;
Future<List<Device>> getDevices([Map<String, dynamic> args]) {
final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
return discoverer.devices;
}).toList();
return new Future<List<Device>>.value(devices);
}
/// Enable device events.
......@@ -603,7 +602,7 @@ class DeviceDomain extends Domain {
final int devicePort = _getIntArg(args, 'devicePort', required: true);
int hostPort = _getIntArg(args, 'hostPort');
final Device device = await daemon.deviceDomain._getDevice(deviceId);
final Device device = daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
......@@ -618,7 +617,7 @@ class DeviceDomain extends Domain {
final int devicePort = _getIntArg(args, 'devicePort', required: true);
final int hostPort = _getIntArg(args, 'hostPort', required: true);
final Device device = await daemon.deviceDomain._getDevice(deviceId);
final Device device = daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
......@@ -632,25 +631,23 @@ class DeviceDomain extends Domain {
}
/// Return the device matching the deviceId field in the args.
Future<Device> _getDevice(String deviceId) async {
for (PollingDeviceDiscovery discoverer in _discoverers) {
final Device device = (await discoverer.devices).firstWhere((Device device) => device.id == deviceId, orElse: () => null);
if (device != null)
return device;
}
return null;
Device _getDevice(String deviceId) {
final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
return discoverer.devices;
}).toList();
return devices.firstWhere((Device device) => device.id == deviceId, orElse: () => null);
}
/// Return a known matching device, or scan for devices if no known match is found.
Future<Device> _getOrLocateDevice(String deviceId) async {
Device _getOrLocateDevice(String deviceId) {
// Look for an already known device.
final Device device = await _getDevice(deviceId);
final Device device = _getDevice(deviceId);
if (device != null)
return device;
// Scan the different device providers for a match.
for (PollingDeviceDiscovery discoverer in _discoverers) {
final List<Device> devices = await discoverer.pollingGetDevices();
final List<Device> devices = discoverer.pollingGetDevices();
for (Device device in devices)
if (device.id == deviceId)
return device;
......
......@@ -81,17 +81,11 @@ class DeviceManager {
: getAllConnectedDevices();
}
Iterable<DeviceDiscovery> get _platformDiscoverers {
return _deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform);
}
/// Return the list of all connected devices.
Stream<Device> getAllConnectedDevices() async* {
for (DeviceDiscovery discoverer in _platformDiscoverers) {
for (Device device in await discoverer.devices) {
yield device;
}
}
Stream<Device> getAllConnectedDevices() {
return new Stream<Device>.fromIterable(_deviceDiscoverers
.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform)
.expand((DeviceDiscovery discoverer) => discoverer.devices));
}
}
......@@ -103,7 +97,7 @@ abstract class DeviceDiscovery {
/// current environment configuration.
bool get canListAnything;
Future<List<Device>> get devices;
List<Device> get devices;
}
/// A [DeviceDiscovery] implementation that uses polling to discover device adds
......@@ -117,13 +111,13 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
ItemListNotifier<Device> _items;
Timer _timer;
Future<List<Device>> pollingGetDevices();
List<Device> pollingGetDevices();
void startPolling() {
if (_timer == null) {
_items ??= new ItemListNotifier<Device>();
_timer = new Timer.periodic(_pollingDuration, (Timer timer) async {
_items.updateWithNewList(await pollingGetDevices());
_timer = new Timer.periodic(_pollingDuration, (Timer timer) {
_items.updateWithNewList(pollingGetDevices());
});
}
}
......@@ -134,8 +128,8 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
}
@override
Future<List<Device>> get devices async {
_items ??= new ItemListNotifier<Device>.from(await pollingGetDevices());
List<Device> get devices {
_items ??= new ItemListNotifier<Device>.from(pollingGetDevices());
return _items.items;
}
......
......@@ -36,11 +36,11 @@ class IOSDevices extends PollingDeviceDiscovery {
bool get canListAnything => iosWorkflow.canListDevices;
@override
Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices();
List<Device> pollingGetDevices() => IOSDevice.getAttachedDevices();
}
class IOSDevice extends Device {
IOSDevice(String id, { this.name, String sdkVersion }) : _sdkVersion = sdkVersion, super(id) {
IOSDevice(String id, { this.name }) : super(id) {
_installerPath = _checkForCommand('ideviceinstaller');
_iproxyPath = _checkForCommand('iproxy');
_pusherPath = _checkForCommand(
......@@ -55,8 +55,6 @@ class IOSDevice extends Device {
String _iproxyPath;
String _pusherPath;
final String _sdkVersion;
@override
bool get supportsHotMode => true;
......@@ -73,30 +71,14 @@ class IOSDevice extends Device {
@override
bool get supportsStartPaused => false;
// Physical device line format to be matched:
// My iPhone (10.3.2) [75b90e947c5f429fa67f3e9169fda0d89f0492f1]
//
// Other formats in output (desktop, simulator) to be ignored:
// my-mac-pro [2C10513E-4dA5-405C-8EF5-C44353DB3ADD]
// iPhone 6s (9.3) [F6CEE7CF-81EB-4448-81B4-1755288C7C11] (Simulator)
static final RegExp _deviceRegex = new RegExp(r'^(.*) +\((.*)\) +\[(.*)\]$');
static Future<List<IOSDevice>> getAttachedDevices() async {
if (!xcode.isInstalled)
static List<IOSDevice> getAttachedDevices() {
if (!iMobileDevice.isInstalled)
return <IOSDevice>[];
final List<IOSDevice> devices = <IOSDevice>[];
final Iterable<String> deviceLines = (await xcode.getAvailableDevices())
.split('\n')
.map((String line) => line.trim());
for (String line in deviceLines) {
final Match match = _deviceRegex.firstMatch(line);
if (match != null) {
final String deviceName = match.group(1);
final String sdkVersion = match.group(2);
final String deviceID = match.group(3);
devices.add(new IOSDevice(deviceID, name: deviceName, sdkVersion: sdkVersion));
}
for (String id in iMobileDevice.getAttachedDeviceIDs()) {
final String name = iMobileDevice.getInfoForDevice(id, 'DeviceName');
devices.add(new IOSDevice(id, name: name));
}
return devices;
}
......@@ -329,7 +311,11 @@ class IOSDevice extends Device {
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion ($_buildVersion)';
String get _sdkVersion => iMobileDevice.getInfoForDevice(id, 'ProductVersion');
String get _buildVersion => iMobileDevice.getInfoForDevice(id, 'BuildVersion');
@override
DeviceLogReader getLogReader({ApplicationPackage app}) {
......
......@@ -70,6 +70,14 @@ class IMobileDevice {
return await exitsHappyAsync(<String>['idevicename']);
}
List<String> getAttachedDeviceIDs() {
return runSync(<String>['idevice_id', '-l'])
.trim()
.split('\n')
.where((String line) => line.isNotEmpty)
.toList();
}
/// Returns the value associated with the specified `ideviceinfo` key for a device.
///
/// If either the specified key or device does not exist, returns the empty string.
......@@ -157,13 +165,6 @@ class Xcode {
return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion);
}
Future<String> getAvailableDevices() async {
final RunResult result = await runAsync(<String>['/usr/bin/instruments', '-s', 'devices']);
if (result.exitCode != 0)
throw new ToolExit('Failed to invoke /usr/bin/instruments. Is Xcode installed?');
return result.stdout;
}
}
bool _xcodeVersionCheckValid(int major, int minor) {
......
......@@ -37,7 +37,7 @@ class IOSSimulators extends PollingDeviceDiscovery {
bool get canListAnything => iosWorkflow.canListDevices;
@override
Future<List<Device>> pollingGetDevices() async => IOSSimulatorUtils.instance.getAttachedDevices();
List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
}
class IOSSimulatorUtils {
......
......@@ -2,8 +2,6 @@
// 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:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/ios/devices.dart';
......@@ -16,7 +14,7 @@ import 'package:test/test.dart';
import '../src/context.dart';
class MockProcessManager extends Mock implements ProcessManager {}
class MockXcode extends Mock implements Xcode {}
class MockIMobileDevice extends Mock implements IMobileDevice {}
class MockFile extends Mock implements File {}
void main() {
......@@ -24,48 +22,46 @@ void main() {
osx.operatingSystem = 'macos';
group('getAttachedDevices', () {
MockXcode mockXcode;
MockIMobileDevice mockIMobileDevice;
setUp(() {
mockXcode = new MockXcode();
mockIMobileDevice = new MockIMobileDevice();
});
testUsingContext('return no devices if Xcode is not installed', () async {
when(mockXcode.isInstalled).thenReturn(false);
expect(await IOSDevice.getAttachedDevices(), isEmpty);
testUsingContext('return no devices if libimobiledevice is not installed', () async {
when(mockIMobileDevice.isInstalled).thenReturn(false);
expect(IOSDevice.getAttachedDevices(), isEmpty);
}, overrides: <Type, Generator>{
Xcode: () => mockXcode,
IMobileDevice: () => mockIMobileDevice,
});
testUsingContext('returns no devices if none are attached', () async {
when(mockXcode.isInstalled).thenReturn(true);
when(mockXcode.getAvailableDevices()).thenReturn(new Future<String>.value(''));
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
when(mockIMobileDevice.isInstalled).thenReturn(true);
when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[]);
final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
expect(devices, isEmpty);
}, overrides: <Type, Generator>{
Xcode: () => mockXcode,
IMobileDevice: () => mockIMobileDevice,
});
testUsingContext('returns attached devices', () async {
when(mockXcode.isInstalled).thenReturn(true);
when(mockXcode.getAvailableDevices()).thenReturn(new Future<String>.value('''
Known Devices:
je-mappelle-horse [ED6552C4-B774-5A4E-8B5A-606710C87C77]
La tele me regarde (10.3.2) [98206e7a4afd4aedaff06e687594e089dede3c44]
Puits sans fond (10.3.2) [f577a7903cc54959be2e34bc4f7f80b7009efcf4]
iPhone 6 Plus (9.3) [FBA880E6-4020-49A5-8083-DCD50CA5FA09] (Simulator)
iPhone 6s (11.0) [E805F496-FC6A-4EA4-92FF-B7901FF4E7CC] (Simulator)
iPhone 7 (11.0) + Apple Watch Series 2 - 38mm (4.0) [60027FDD-4A7A-42BF-978F-C2209D27AD61] (Simulator)
iPhone SE (11.0) [667E8DCD-5DCD-4C80-93A9-60D1D995206F] (Simulator)
'''));
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
when(mockIMobileDevice.isInstalled).thenReturn(true);
when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[
'98206e7a4afd4aedaff06e687594e089dede3c44',
'f577a7903cc54959be2e34bc4f7f80b7009efcf4',
]);
when(mockIMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
.thenReturn('La tele me regarde');
when(mockIMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
.thenReturn('Puits sans fond');
final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
expect(devices, hasLength(2));
expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
expect(devices[0].name, 'La tele me regarde');
expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
expect(devices[1].name, 'Puits sans fond');
}, overrides: <Type, Generator>{
Xcode: () => mockXcode,
IMobileDevice: () => mockIMobileDevice,
});
});
......
......@@ -37,7 +37,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
MockPollingDeviceDiscovery() : super('mock');
@override
Future<List<Device>> pollingGetDevices() async => _devices;
List<Device> pollingGetDevices() => _devices;
@override
bool get supportsPlatform => true;
......@@ -52,7 +52,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
}
@override
Future<List<Device>> get devices async => _devices;
List<Device> get devices => _devices;
@override
Stream<Device> get onAdded => _onAddedController.stream;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment