mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
213 lines
7.8 KiB
Dart
213 lines
7.8 KiB
Dart
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import '../base/context.dart';
|
|
import '../base/os.dart';
|
|
import '../base/platform.dart';
|
|
import '../base/process.dart';
|
|
import '../base/user_messages.dart';
|
|
import '../base/version.dart';
|
|
import '../doctor.dart';
|
|
import 'cocoapods.dart';
|
|
import 'mac.dart';
|
|
import 'plist_utils.dart' as plist;
|
|
|
|
IOSWorkflow get iosWorkflow => context[IOSWorkflow];
|
|
IOSValidator get iosValidator => context[IOSValidator];
|
|
CocoaPodsValidator get cocoapodsValidator => context[CocoaPodsValidator];
|
|
|
|
class IOSWorkflow implements Workflow {
|
|
const IOSWorkflow();
|
|
|
|
@override
|
|
bool get appliesToHostPlatform => platform.isMacOS;
|
|
|
|
// We need xcode (+simctl) to list simulator devices, and libimobiledevice to list real devices.
|
|
@override
|
|
bool get canListDevices => xcode.isInstalledAndMeetsVersionCheck && xcode.isSimctlInstalled;
|
|
|
|
// We need xcode to launch simulator devices, and ideviceinstaller and ios-deploy
|
|
// for real devices.
|
|
@override
|
|
bool get canLaunchDevices => xcode.isInstalledAndMeetsVersionCheck;
|
|
|
|
@override
|
|
bool get canListEmulators => false;
|
|
|
|
String getPlistValueFromFile(String path, String key) {
|
|
return plist.getValueFromFile(path, key);
|
|
}
|
|
}
|
|
|
|
class IOSValidator extends DoctorValidator {
|
|
|
|
const IOSValidator() : super('iOS toolchain - develop for iOS devices');
|
|
|
|
Future<bool> get hasIDeviceInstaller => exitsHappyAsync(<String>['ideviceinstaller', '-h']);
|
|
|
|
Future<bool> get hasIosDeploy => exitsHappyAsync(<String>['ios-deploy', '--version']);
|
|
|
|
String get iosDeployMinimumVersion => '1.9.4';
|
|
|
|
Future<String> get iosDeployVersionText async => (await runAsync(<String>['ios-deploy', '--version'])).processResult.stdout.replaceAll('\n', '');
|
|
|
|
bool get hasHomebrew => os.which('brew') != null;
|
|
|
|
Future<String> get macDevMode async => (await runAsync(<String>['DevToolsSecurity', '-status'])).processResult.stdout;
|
|
|
|
Future<bool> get _iosDeployIsInstalledAndMeetsVersionCheck async {
|
|
if (!await hasIosDeploy)
|
|
return false;
|
|
try {
|
|
final Version version = Version.parse(await iosDeployVersionText);
|
|
return version >= Version.parse(iosDeployMinimumVersion);
|
|
} on FormatException catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Change this value if the number of checks for packages needed for installation changes
|
|
static const int totalChecks = 4;
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
final List<ValidationMessage> messages = <ValidationMessage>[];
|
|
ValidationType xcodeStatus = ValidationType.missing;
|
|
ValidationType packageManagerStatus = ValidationType.installed;
|
|
String xcodeVersionInfo;
|
|
|
|
if (xcode.isInstalled) {
|
|
xcodeStatus = ValidationType.installed;
|
|
|
|
messages.add(ValidationMessage(userMessages.iOSXcodeLocation(xcode.xcodeSelectPath)));
|
|
|
|
xcodeVersionInfo = xcode.versionText;
|
|
if (xcodeVersionInfo.contains(','))
|
|
xcodeVersionInfo = xcodeVersionInfo.substring(0, xcodeVersionInfo.indexOf(','));
|
|
messages.add(ValidationMessage(xcode.versionText));
|
|
|
|
if (!xcode.isInstalledAndMeetsVersionCheck) {
|
|
xcodeStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(
|
|
userMessages.iOSXcodeOutdated(kXcodeRequiredVersionMajor, kXcodeRequiredVersionMinor)
|
|
));
|
|
}
|
|
|
|
if (!xcode.eulaSigned) {
|
|
xcodeStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.iOSXcodeEula));
|
|
}
|
|
if (!xcode.isSimctlInstalled) {
|
|
xcodeStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.iOSXcodeMissingSimct));
|
|
}
|
|
|
|
} else {
|
|
xcodeStatus = ValidationType.missing;
|
|
if (xcode.xcodeSelectPath == null || xcode.xcodeSelectPath.isEmpty) {
|
|
messages.add(ValidationMessage.error(userMessages.iOSXcodeMissing));
|
|
} else {
|
|
messages.add(ValidationMessage.error(userMessages.iOSXcodeIncomplete));
|
|
}
|
|
}
|
|
|
|
int checksFailed = 0;
|
|
|
|
if (!iMobileDevice.isInstalled) {
|
|
checksFailed += 3;
|
|
packageManagerStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.iOSIMobileDeviceMissing));
|
|
} else if (!await iMobileDevice.isWorking) {
|
|
checksFailed += 2;
|
|
packageManagerStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.iOSIMobileDeviceBroken));
|
|
} else if (!await hasIDeviceInstaller) {
|
|
checksFailed += 1;
|
|
packageManagerStatus = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.iOSDeviceInstallerMissing));
|
|
}
|
|
|
|
final bool iHasIosDeploy = await hasIosDeploy;
|
|
|
|
// Check ios-deploy is installed at meets version requirements.
|
|
if (iHasIosDeploy) {
|
|
messages.add(ValidationMessage(userMessages.iOSDeployVersion(await iosDeployVersionText)));
|
|
}
|
|
if (!await _iosDeployIsInstalledAndMeetsVersionCheck) {
|
|
packageManagerStatus = ValidationType.partial;
|
|
if (iHasIosDeploy) {
|
|
messages.add(ValidationMessage.error(userMessages.iOSDeployOutdated(iosDeployMinimumVersion)));
|
|
} else {
|
|
checksFailed += 1;
|
|
messages.add(ValidationMessage.error(userMessages.iOSDeployMissing));
|
|
}
|
|
}
|
|
|
|
// If one of the checks for the packages failed, we may need brew so that we can install
|
|
// the necessary packages. If they're all there, however, we don't even need it.
|
|
if (checksFailed == totalChecks)
|
|
packageManagerStatus = ValidationType.missing;
|
|
if (checksFailed > 0 && !hasHomebrew) {
|
|
messages.add(ValidationMessage.error(userMessages.iOSBrewMissing));
|
|
}
|
|
|
|
return ValidationResult(
|
|
<ValidationType>[xcodeStatus, packageManagerStatus].reduce(_mergeValidationTypes),
|
|
messages,
|
|
statusInfo: xcodeVersionInfo
|
|
);
|
|
}
|
|
|
|
ValidationType _mergeValidationTypes(ValidationType t1, ValidationType t2) {
|
|
return t1 == t2 ? t1 : ValidationType.partial;
|
|
}
|
|
}
|
|
|
|
class CocoaPodsValidator extends DoctorValidator {
|
|
const CocoaPodsValidator() : super('CocoaPods subvalidator');
|
|
|
|
bool get hasHomebrew => os.which('brew') != null;
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
final List<ValidationMessage> messages = <ValidationMessage>[];
|
|
|
|
ValidationType status = ValidationType.installed;
|
|
if (hasHomebrew) {
|
|
final CocoaPodsStatus cocoaPodsStatus = await cocoaPods
|
|
.evaluateCocoaPodsInstallation;
|
|
|
|
if (cocoaPodsStatus == CocoaPodsStatus.recommended) {
|
|
if (await cocoaPods.isCocoaPodsInitialized) {
|
|
messages.add(ValidationMessage(userMessages.cocoaPodsVersion(await cocoaPods.cocoaPodsVersionText)));
|
|
} else {
|
|
status = ValidationType.partial;
|
|
messages.add(ValidationMessage.error(userMessages.cocoaPodsUninitialized(noCocoaPodsConsequence)));
|
|
}
|
|
} else {
|
|
if (cocoaPodsStatus == CocoaPodsStatus.notInstalled) {
|
|
status = ValidationType.missing;
|
|
messages.add(ValidationMessage.error(
|
|
userMessages.cocoaPodsMissing(noCocoaPodsConsequence, cocoaPodsInstallInstructions)));
|
|
}
|
|
else if (cocoaPodsStatus == CocoaPodsStatus.unknownVersion) {
|
|
status = ValidationType.partial;
|
|
messages.add(ValidationMessage.hint(
|
|
userMessages.cocoaPodsUnknownVersion(unknownCocoaPodsConsequence, cocoaPodsUpgradeInstructions)));
|
|
} else {
|
|
status = ValidationType.partial;
|
|
messages.add(ValidationMessage.hint(
|
|
userMessages.cocoaPodsOutdated(cocoaPods.cocoaPodsRecommendedVersion, noCocoaPodsConsequence, cocoaPodsUpgradeInstructions)));
|
|
}
|
|
}
|
|
} else {
|
|
// Only set status. The main validator handles messages for missing brew.
|
|
status = ValidationType.missing;
|
|
}
|
|
return ValidationResult(status, messages);
|
|
}
|
|
}
|