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