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

Make most ios_workflow host tool calls async (#10260)

Does not yet migrate hasIdeviceId since that results in a cascade of
breaking interface changes that's significant enough for a separate
patch.
parent 9a908f75
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
...@@ -32,53 +33,55 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -32,53 +33,55 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
bool get hasIDeviceId => exitsHappy(<String>['idevice_id', '-h']); bool get hasIDeviceId => exitsHappy(<String>['idevice_id', '-h']);
bool get hasWorkingLibimobiledevice { Future<bool> get hasWorkingLibimobiledevice async {
// Verify that libimobiledevice tools are installed. // Verify that libimobiledevice tools are installed.
if (!hasIDeviceId) if (!hasIDeviceId)
return false; return false;
// If a device is attached, verify that we can get its name. // If a device is attached, verify that we can get its name.
final String result = runSync(<String>['idevice_id', '-l']); final ProcessResult result = (await runAsync(<String>['idevice_id', '-l'])).processResult;
if (result.isNotEmpty && !exitsHappy(<String>['idevicename'])) if (result.exitCode == 0 && result.stdout.isNotEmpty && !await exitsHappyAsync(<String>['idevicename']))
return false; return false;
return true; return true;
} }
bool get hasIDeviceInstaller => exitsHappy(<String>['ideviceinstaller', '-h']); Future<bool> get hasIDeviceInstaller => exitsHappyAsync(<String>['ideviceinstaller', '-h']);
bool get hasIosDeploy => exitsHappy(<String>['ios-deploy', '--version']); Future<bool> get hasIosDeploy => exitsHappyAsync(<String>['ios-deploy', '--version']);
String get iosDeployMinimumVersion => '1.9.0'; String get iosDeployMinimumVersion => '1.9.0';
String get iosDeployVersionText => runSync(<String>['ios-deploy', '--version']).replaceAll('\n', ''); Future<String> get iosDeployVersionText async =>
(await runAsync(<String>['ios-deploy', '--version'])).processResult.stdout.replaceAll('\n', '');
bool get hasHomebrew => os.which('brew') != null; bool get hasHomebrew => os.which('brew') != null;
bool get hasPythonSixModule => kPythonSix.isInstalled; bool get hasPythonSixModule => kPythonSix.isInstalled;
bool get hasCocoaPods => exitsHappy(<String>['pod', '--version']); Future<bool> get hasCocoaPods => exitsHappyAsync(<String>['pod', '--version']);
String get cocoaPodsMinimumVersion => '1.0.0'; String get cocoaPodsMinimumVersion => '1.0.0';
String get cocoaPodsVersionText => runSync(<String>['pod', '--version']).trim(); Future<String> get cocoaPodsVersionText async =>
(await runAsync(<String>['pod', '--version'])).processResult.stdout.trim();
bool get _iosDeployIsInstalledAndMeetsVersionCheck { Future<bool> get _iosDeployIsInstalledAndMeetsVersionCheck async {
if (!hasIosDeploy) if (!await hasIosDeploy)
return false; return false;
try { try {
final Version version = new Version.parse(iosDeployVersionText); final Version version = new Version.parse(await iosDeployVersionText);
return version >= new Version.parse(iosDeployMinimumVersion); return version >= new Version.parse(iosDeployMinimumVersion);
} on FormatException catch (_) { } on FormatException catch (_) {
return false; return false;
} }
} }
bool get isCocoaPodsInstalledAndMeetsVersionCheck { Future<bool> get isCocoaPodsInstalledAndMeetsVersionCheck async {
if (!hasCocoaPods) if (!await hasCocoaPods)
return false; return false;
try { try {
final Version installedVersion = new Version.parse(cocoaPodsVersionText); final Version installedVersion = new Version.parse(await cocoaPodsVersionText);
return installedVersion >= new Version.parse(cocoaPodsMinimumVersion); return installedVersion >= new Version.parse(cocoaPodsMinimumVersion);
} on FormatException { } on FormatException {
return false; return false;
...@@ -86,11 +89,8 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -86,11 +89,8 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
} }
/// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are cloned. /// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are cloned.
bool get isCocoaPodsInitialized { Future<bool> get isCocoaPodsInitialized =>
return fs.isDirectorySync( fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master'));
fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master')
);
}
@override @override
Future<ValidationResult> validate() async { Future<ValidationResult> validate() async {
...@@ -152,7 +152,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -152,7 +152,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
if (hasHomebrew) { if (hasHomebrew) {
brewStatus = ValidationType.installed; brewStatus = ValidationType.installed;
if (!hasWorkingLibimobiledevice) { if (!await hasWorkingLibimobiledevice) {
brewStatus = ValidationType.partial; brewStatus = ValidationType.partial;
messages.add(new ValidationMessage.error( messages.add(new ValidationMessage.error(
'libimobiledevice is incompatible with the installed Xcode version. To update, run:\n' 'libimobiledevice is incompatible with the installed Xcode version. To update, run:\n'
...@@ -162,7 +162,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -162,7 +162,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
)); ));
} }
if (!hasIDeviceInstaller) { if (!await hasIDeviceInstaller) {
brewStatus = ValidationType.partial; brewStatus = ValidationType.partial;
messages.add(new ValidationMessage.error( messages.add(new ValidationMessage.error(
'ideviceinstaller not available; this is used to discover connected iOS devices.\n' 'ideviceinstaller not available; this is used to discover connected iOS devices.\n'
...@@ -174,12 +174,12 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -174,12 +174,12 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
} }
// Check ios-deploy is installed at meets version requirements. // Check ios-deploy is installed at meets version requirements.
if (hasIosDeploy) { if (await hasIosDeploy) {
messages.add(new ValidationMessage('ios-deploy $iosDeployVersionText')); messages.add(new ValidationMessage('ios-deploy ${await iosDeployVersionText}'));
} }
if (!_iosDeployIsInstalledAndMeetsVersionCheck) { if (!await _iosDeployIsInstalledAndMeetsVersionCheck) {
brewStatus = ValidationType.partial; brewStatus = ValidationType.partial;
if (hasIosDeploy) { if (await hasIosDeploy) {
messages.add(new ValidationMessage.error( messages.add(new ValidationMessage.error(
'ios-deploy out of date ($iosDeployMinimumVersion is required). To upgrade:\n' 'ios-deploy out of date ($iosDeployMinimumVersion is required). To upgrade:\n'
' brew update\n' ' brew update\n'
...@@ -194,9 +194,9 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -194,9 +194,9 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
} }
} }
if (isCocoaPodsInstalledAndMeetsVersionCheck) { if (await isCocoaPodsInstalledAndMeetsVersionCheck) {
if (isCocoaPodsInitialized) { if (await isCocoaPodsInitialized) {
messages.add(new ValidationMessage('CocoaPods version $cocoaPodsVersionText')); messages.add(new ValidationMessage('CocoaPods version ${await cocoaPodsVersionText}'));
} else { } else {
brewStatus = ValidationType.partial; brewStatus = ValidationType.partial;
messages.add(new ValidationMessage.error( messages.add(new ValidationMessage.error(
...@@ -209,7 +209,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow { ...@@ -209,7 +209,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
} }
} else { } else {
brewStatus = ValidationType.partial; brewStatus = ValidationType.partial;
if (!hasCocoaPods) { if (!await hasCocoaPods) {
messages.add(new ValidationMessage.error( messages.add(new ValidationMessage.error(
'CocoaPods not installed.\n' 'CocoaPods not installed.\n'
'$noCocoaPodsConsequence\n' '$noCocoaPodsConsequence\n'
......
...@@ -365,7 +365,7 @@ final String cocoaPodsUpgradeInstructions = ''' ...@@ -365,7 +365,7 @@ final String cocoaPodsUpgradeInstructions = '''
Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async { Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async {
if (fs.file(fs.path.join(bundle.path, 'Podfile')).existsSync()) { if (fs.file(fs.path.join(bundle.path, 'Podfile')).existsSync()) {
if (!doctor.iosWorkflow.isCocoaPodsInstalledAndMeetsVersionCheck) { if (!await doctor.iosWorkflow.isCocoaPodsInstalledAndMeetsVersionCheck) {
final String minimumVersion = doctor.iosWorkflow.cocoaPodsMinimumVersion; final String minimumVersion = doctor.iosWorkflow.cocoaPodsMinimumVersion;
printError( printError(
'Warning: CocoaPods version $minimumVersion or greater not installed. Skipping pod install.\n' 'Warning: CocoaPods version $minimumVersion or greater not installed. Skipping pod install.\n'
...@@ -376,7 +376,7 @@ Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async { ...@@ -376,7 +376,7 @@ Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async {
); );
return; return;
} }
if (!doctor.iosWorkflow.isCocoaPodsInitialized) { if (!await doctor.iosWorkflow.isCocoaPodsInitialized) {
printError( printError(
'Warning: CocoaPods installed but not initialized. Skipping pod install.\n' 'Warning: CocoaPods installed but not initialized. Skipping pod install.\n'
'$noCocoaPodsConsequence\n' '$noCocoaPodsConsequence\n'
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// 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/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
...@@ -30,10 +32,11 @@ void main() { ...@@ -30,10 +32,11 @@ void main() {
testUsingContext('Emit missing status when nothing is installed', () async { testUsingContext('Emit missing status when nothing is installed', () async {
when(xcode.isInstalled).thenReturn(false); when(xcode.isInstalled).thenReturn(false);
when(xcode.xcodeSelectPath).thenReturn(null); when(xcode.xcodeSelectPath).thenReturn(null);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(
..hasPythonSixModule = false hasPythonSixModule: false,
..hasHomebrew = false hasHomebrew: false,
..hasIosDeploy = false; hasIosDeploy: false,
);
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.missing); expect(result.type, ValidationType.missing);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -82,8 +85,7 @@ void main() { ...@@ -82,8 +85,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasPythonSixModule: false);
..hasPythonSixModule = false;
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -94,8 +96,7 @@ void main() { ...@@ -94,8 +96,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasHomebrew: false);
..hasHomebrew = false;
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -106,8 +107,7 @@ void main() { ...@@ -106,8 +107,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasWorkingLibimobiledevice: false);
..hasWorkingLibimobiledevice = false;
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -118,8 +118,7 @@ void main() { ...@@ -118,8 +118,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasIosDeploy: false);
..hasIosDeploy = false;
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -130,8 +129,7 @@ void main() { ...@@ -130,8 +129,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(iosDeployVersionText: '1.8.0');
..iosDeployVersionText = '1.8.0';
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -142,8 +140,7 @@ void main() { ...@@ -142,8 +140,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasCocoaPods: false);
..hasCocoaPods = false;
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -154,8 +151,7 @@ void main() { ...@@ -154,8 +151,7 @@ void main() {
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget() final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(cocoaPodsVersionText: '0.39.0');
..cocoaPodsVersionText = '0.39.0';
final ValidationResult result = await workflow.validate(); final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{ Xcode: () => xcode }); }, overrides: <Type, Generator>{ Xcode: () => xcode });
...@@ -215,27 +211,43 @@ class MockXcode extends Mock implements Xcode {} ...@@ -215,27 +211,43 @@ class MockXcode extends Mock implements Xcode {}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class IOSWorkflowTestTarget extends IOSWorkflow { class IOSWorkflowTestTarget extends IOSWorkflow {
IOSWorkflowTestTarget({
this.hasPythonSixModule: true,
this.hasHomebrew: true,
bool hasWorkingLibimobiledevice: true,
bool hasIosDeploy: true,
String iosDeployVersionText: '1.9.0',
bool hasIDeviceInstaller: true,
bool hasCocoaPods: true,
String cocoaPodsVersionText: '1.2.0',
}) : hasWorkingLibimobiledevice = new Future<bool>.value(hasWorkingLibimobiledevice),
hasIosDeploy = new Future<bool>.value(hasIosDeploy),
iosDeployVersionText = new Future<String>.value(iosDeployVersionText),
hasIDeviceInstaller = new Future<bool>.value(hasIDeviceInstaller),
hasCocoaPods = new Future<bool>.value(hasCocoaPods),
cocoaPodsVersionText = new Future<String>.value(cocoaPodsVersionText);
@override @override
bool hasPythonSixModule = true; final bool hasPythonSixModule;
@override @override
bool hasHomebrew = true; final bool hasHomebrew;
@override @override
bool hasWorkingLibimobiledevice = true; final Future<bool> hasWorkingLibimobiledevice;
@override @override
bool hasIosDeploy = true; final Future<bool> hasIosDeploy;
@override @override
String iosDeployVersionText = '1.9.0'; final Future<String> iosDeployVersionText;
@override @override
bool get hasIDeviceInstaller => true; final Future<bool> hasIDeviceInstaller;
@override @override
bool hasCocoaPods = true; final Future<bool> hasCocoaPods;
@override @override
String cocoaPodsVersionText = '1.2.0'; final Future<String> cocoaPodsVersionText;
} }
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