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 { ...@@ -48,7 +48,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
bool get canListAnything => androidWorkflow.canListDevices; bool get canListAnything => androidWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async => getAdbDevices(); List<Device> pollingGetDevices() => getAdbDevices();
} }
class AndroidDevice extends Device { class AndroidDevice extends Device {
......
...@@ -305,7 +305,7 @@ class AppDomain extends Domain { ...@@ -305,7 +305,7 @@ class AppDomain extends Domain {
final String target = _getStringArg(args, 'target'); final String target = _getStringArg(args, 'target');
final bool enableHotReload = _getBoolArg(args, 'hot') ?? kHotReloadDefault; 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) if (device == null)
throw "device '$deviceId' not found"; throw "device '$deviceId' not found";
...@@ -493,7 +493,7 @@ class AppDomain extends Domain { ...@@ -493,7 +493,7 @@ class AppDomain extends Domain {
Future<List<Map<String, dynamic>>> discover(Map<String, dynamic> args) async { Future<List<Map<String, dynamic>>> discover(Map<String, dynamic> args) async {
final String deviceId = _getStringArg(args, 'deviceId', required: true); 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) if (device == null)
throw "device '$deviceId' not found"; throw "device '$deviceId' not found";
...@@ -575,12 +575,11 @@ class DeviceDomain extends Domain { ...@@ -575,12 +575,11 @@ class DeviceDomain extends Domain {
final List<PollingDeviceDiscovery> _discoverers = <PollingDeviceDiscovery>[]; final List<PollingDeviceDiscovery> _discoverers = <PollingDeviceDiscovery>[];
Future<List<Device>> getDevices([Map<String, dynamic> args]) async { Future<List<Device>> getDevices([Map<String, dynamic> args]) {
final List<Device> devices = <Device>[]; final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
for (PollingDeviceDiscovery discoverer in _discoverers) { return discoverer.devices;
devices.addAll(await discoverer.devices); }).toList();
} return new Future<List<Device>>.value(devices);
return devices;
} }
/// Enable device events. /// Enable device events.
...@@ -603,7 +602,7 @@ class DeviceDomain extends Domain { ...@@ -603,7 +602,7 @@ class DeviceDomain extends Domain {
final int devicePort = _getIntArg(args, 'devicePort', required: true); final int devicePort = _getIntArg(args, 'devicePort', required: true);
int hostPort = _getIntArg(args, 'hostPort'); int hostPort = _getIntArg(args, 'hostPort');
final Device device = await daemon.deviceDomain._getDevice(deviceId); final Device device = daemon.deviceDomain._getDevice(deviceId);
if (device == null) if (device == null)
throw "device '$deviceId' not found"; throw "device '$deviceId' not found";
...@@ -618,7 +617,7 @@ class DeviceDomain extends Domain { ...@@ -618,7 +617,7 @@ class DeviceDomain extends Domain {
final int devicePort = _getIntArg(args, 'devicePort', required: true); final int devicePort = _getIntArg(args, 'devicePort', required: true);
final int hostPort = _getIntArg(args, 'hostPort', 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) if (device == null)
throw "device '$deviceId' not found"; throw "device '$deviceId' not found";
...@@ -632,25 +631,23 @@ class DeviceDomain extends Domain { ...@@ -632,25 +631,23 @@ class DeviceDomain extends Domain {
} }
/// Return the device matching the deviceId field in the args. /// Return the device matching the deviceId field in the args.
Future<Device> _getDevice(String deviceId) async { Device _getDevice(String deviceId) {
for (PollingDeviceDiscovery discoverer in _discoverers) { final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
final Device device = (await discoverer.devices).firstWhere((Device device) => device.id == deviceId, orElse: () => null); return discoverer.devices;
if (device != null) }).toList();
return device; return devices.firstWhere((Device device) => device.id == deviceId, orElse: () => null);
}
return null;
} }
/// Return a known matching device, or scan for devices if no known match is found. /// 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. // Look for an already known device.
final Device device = await _getDevice(deviceId); final Device device = _getDevice(deviceId);
if (device != null) if (device != null)
return device; return device;
// Scan the different device providers for a match. // Scan the different device providers for a match.
for (PollingDeviceDiscovery discoverer in _discoverers) { for (PollingDeviceDiscovery discoverer in _discoverers) {
final List<Device> devices = await discoverer.pollingGetDevices(); final List<Device> devices = discoverer.pollingGetDevices();
for (Device device in devices) for (Device device in devices)
if (device.id == deviceId) if (device.id == deviceId)
return device; return device;
......
...@@ -81,17 +81,11 @@ class DeviceManager { ...@@ -81,17 +81,11 @@ class DeviceManager {
: getAllConnectedDevices(); : getAllConnectedDevices();
} }
Iterable<DeviceDiscovery> get _platformDiscoverers {
return _deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform);
}
/// Return the list of all connected devices. /// Return the list of all connected devices.
Stream<Device> getAllConnectedDevices() async* { Stream<Device> getAllConnectedDevices() {
for (DeviceDiscovery discoverer in _platformDiscoverers) { return new Stream<Device>.fromIterable(_deviceDiscoverers
for (Device device in await discoverer.devices) { .where((DeviceDiscovery discoverer) => discoverer.supportsPlatform)
yield device; .expand((DeviceDiscovery discoverer) => discoverer.devices));
}
}
} }
} }
...@@ -103,7 +97,7 @@ abstract class DeviceDiscovery { ...@@ -103,7 +97,7 @@ abstract class DeviceDiscovery {
/// current environment configuration. /// current environment configuration.
bool get canListAnything; bool get canListAnything;
Future<List<Device>> get devices; List<Device> get devices;
} }
/// A [DeviceDiscovery] implementation that uses polling to discover device adds /// A [DeviceDiscovery] implementation that uses polling to discover device adds
...@@ -117,13 +111,13 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -117,13 +111,13 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
ItemListNotifier<Device> _items; ItemListNotifier<Device> _items;
Timer _timer; Timer _timer;
Future<List<Device>> pollingGetDevices(); List<Device> pollingGetDevices();
void startPolling() { void startPolling() {
if (_timer == null) { if (_timer == null) {
_items ??= new ItemListNotifier<Device>(); _items ??= new ItemListNotifier<Device>();
_timer = new Timer.periodic(_pollingDuration, (Timer timer) async { _timer = new Timer.periodic(_pollingDuration, (Timer timer) {
_items.updateWithNewList(await pollingGetDevices()); _items.updateWithNewList(pollingGetDevices());
}); });
} }
} }
...@@ -134,8 +128,8 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -134,8 +128,8 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
} }
@override @override
Future<List<Device>> get devices async { List<Device> get devices {
_items ??= new ItemListNotifier<Device>.from(await pollingGetDevices()); _items ??= new ItemListNotifier<Device>.from(pollingGetDevices());
return _items.items; return _items.items;
} }
......
...@@ -36,11 +36,11 @@ class IOSDevices extends PollingDeviceDiscovery { ...@@ -36,11 +36,11 @@ class IOSDevices extends PollingDeviceDiscovery {
bool get canListAnything => iosWorkflow.canListDevices; bool get canListAnything => iosWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices(); List<Device> pollingGetDevices() => IOSDevice.getAttachedDevices();
} }
class IOSDevice extends Device { 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'); _installerPath = _checkForCommand('ideviceinstaller');
_iproxyPath = _checkForCommand('iproxy'); _iproxyPath = _checkForCommand('iproxy');
_pusherPath = _checkForCommand( _pusherPath = _checkForCommand(
...@@ -55,8 +55,6 @@ class IOSDevice extends Device { ...@@ -55,8 +55,6 @@ class IOSDevice extends Device {
String _iproxyPath; String _iproxyPath;
String _pusherPath; String _pusherPath;
final String _sdkVersion;
@override @override
bool get supportsHotMode => true; bool get supportsHotMode => true;
...@@ -73,30 +71,14 @@ class IOSDevice extends Device { ...@@ -73,30 +71,14 @@ class IOSDevice extends Device {
@override @override
bool get supportsStartPaused => false; bool get supportsStartPaused => false;
// Physical device line format to be matched: static List<IOSDevice> getAttachedDevices() {
// My iPhone (10.3.2) [75b90e947c5f429fa67f3e9169fda0d89f0492f1] if (!iMobileDevice.isInstalled)
//
// 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)
return <IOSDevice>[]; return <IOSDevice>[];
final List<IOSDevice> devices = <IOSDevice>[]; final List<IOSDevice> devices = <IOSDevice>[];
final Iterable<String> deviceLines = (await xcode.getAvailableDevices()) for (String id in iMobileDevice.getAttachedDeviceIDs()) {
.split('\n') final String name = iMobileDevice.getInfoForDevice(id, 'DeviceName');
.map((String line) => line.trim()); devices.add(new IOSDevice(id, name: name));
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));
}
} }
return devices; return devices;
} }
...@@ -329,7 +311,11 @@ class IOSDevice extends Device { ...@@ -329,7 +311,11 @@ class IOSDevice extends Device {
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override @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 @override
DeviceLogReader getLogReader({ApplicationPackage app}) { DeviceLogReader getLogReader({ApplicationPackage app}) {
......
...@@ -70,6 +70,14 @@ class IMobileDevice { ...@@ -70,6 +70,14 @@ class IMobileDevice {
return await exitsHappyAsync(<String>['idevicename']); 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. /// 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. /// If either the specified key or device does not exist, returns the empty string.
...@@ -157,13 +165,6 @@ class Xcode { ...@@ -157,13 +165,6 @@ class Xcode {
return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion); 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) { bool _xcodeVersionCheckValid(int major, int minor) {
......
...@@ -37,7 +37,7 @@ class IOSSimulators extends PollingDeviceDiscovery { ...@@ -37,7 +37,7 @@ class IOSSimulators extends PollingDeviceDiscovery {
bool get canListAnything => iosWorkflow.canListDevices; bool get canListAnything => iosWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async => IOSSimulatorUtils.instance.getAttachedDevices(); List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
} }
class IOSSimulatorUtils { class IOSSimulatorUtils {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
...@@ -16,7 +14,7 @@ import 'package:test/test.dart'; ...@@ -16,7 +14,7 @@ import 'package:test/test.dart';
import '../src/context.dart'; import '../src/context.dart';
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockXcode extends Mock implements Xcode {} class MockIMobileDevice extends Mock implements IMobileDevice {}
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
void main() { void main() {
...@@ -24,48 +22,46 @@ void main() { ...@@ -24,48 +22,46 @@ void main() {
osx.operatingSystem = 'macos'; osx.operatingSystem = 'macos';
group('getAttachedDevices', () { group('getAttachedDevices', () {
MockXcode mockXcode; MockIMobileDevice mockIMobileDevice;
setUp(() { setUp(() {
mockXcode = new MockXcode(); mockIMobileDevice = new MockIMobileDevice();
}); });
testUsingContext('return no devices if Xcode is not installed', () async { testUsingContext('return no devices if libimobiledevice is not installed', () async {
when(mockXcode.isInstalled).thenReturn(false); when(mockIMobileDevice.isInstalled).thenReturn(false);
expect(await IOSDevice.getAttachedDevices(), isEmpty); expect(IOSDevice.getAttachedDevices(), isEmpty);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Xcode: () => mockXcode, IMobileDevice: () => mockIMobileDevice,
}); });
testUsingContext('returns no devices if none are attached', () async { testUsingContext('returns no devices if none are attached', () async {
when(mockXcode.isInstalled).thenReturn(true); when(mockIMobileDevice.isInstalled).thenReturn(true);
when(mockXcode.getAvailableDevices()).thenReturn(new Future<String>.value('')); when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[]);
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices(); final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
expect(devices, isEmpty); expect(devices, isEmpty);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Xcode: () => mockXcode, IMobileDevice: () => mockIMobileDevice,
}); });
testUsingContext('returns attached devices', () async { testUsingContext('returns attached devices', () async {
when(mockXcode.isInstalled).thenReturn(true); when(mockIMobileDevice.isInstalled).thenReturn(true);
when(mockXcode.getAvailableDevices()).thenReturn(new Future<String>.value(''' when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[
Known Devices: '98206e7a4afd4aedaff06e687594e089dede3c44',
je-mappelle-horse [ED6552C4-B774-5A4E-8B5A-606710C87C77] 'f577a7903cc54959be2e34bc4f7f80b7009efcf4',
La tele me regarde (10.3.2) [98206e7a4afd4aedaff06e687594e089dede3c44] ]);
Puits sans fond (10.3.2) [f577a7903cc54959be2e34bc4f7f80b7009efcf4] when(mockIMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
iPhone 6 Plus (9.3) [FBA880E6-4020-49A5-8083-DCD50CA5FA09] (Simulator) .thenReturn('La tele me regarde');
iPhone 6s (11.0) [E805F496-FC6A-4EA4-92FF-B7901FF4E7CC] (Simulator) when(mockIMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
iPhone 7 (11.0) + Apple Watch Series 2 - 38mm (4.0) [60027FDD-4A7A-42BF-978F-C2209D27AD61] (Simulator) .thenReturn('Puits sans fond');
iPhone SE (11.0) [667E8DCD-5DCD-4C80-93A9-60D1D995206F] (Simulator) final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
'''));
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
expect(devices, hasLength(2)); expect(devices, hasLength(2));
expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44'); expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
expect(devices[0].name, 'La tele me regarde'); expect(devices[0].name, 'La tele me regarde');
expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4'); expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
expect(devices[1].name, 'Puits sans fond'); expect(devices[1].name, 'Puits sans fond');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Xcode: () => mockXcode, IMobileDevice: () => mockIMobileDevice,
}); });
}); });
......
...@@ -37,7 +37,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -37,7 +37,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
MockPollingDeviceDiscovery() : super('mock'); MockPollingDeviceDiscovery() : super('mock');
@override @override
Future<List<Device>> pollingGetDevices() async => _devices; List<Device> pollingGetDevices() => _devices;
@override @override
bool get supportsPlatform => true; bool get supportsPlatform => true;
...@@ -52,7 +52,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -52,7 +52,7 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
} }
@override @override
Future<List<Device>> get devices async => _devices; List<Device> get devices => _devices;
@override @override
Stream<Device> get onAdded => _onAddedController.stream; 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