Files
flutter/packages/flutter_tools/lib/src/macos/xcode_validator.dart
Victoria Ashworth b39604e02d Check for simulator runtime in flutter doctor (#131795)
Redo of https://github.com/flutter/flutter/pull/130728 - code is the same as before. That PR was stuck in Google testing and then I messed up the rebase so started over.

----

Starting in Xcode 15, the simulator is no longer included in Xcode and must be downloaded and installed separately. This adds a validation to `flutter doctor` to warn when the needed simulator runtime is missing.

Validation message looks like:
```
[!] Xcode - develop for iOS and macOS (Xcode 15.0)
    ! iOS 17.0 Simulator not installed; this may be necessary for iOS and macOS development.
      To download and install the platform, open Xcode, select Xcode > Settings > Platforms,
      and click the GET button for the required platform.

      For more information, please visit:
        https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes
```

It may also show an error like this when something goes wrong when checking for the simulator:
```
[!] Xcode - develop for iOS and macOS (Xcode 15.0)
    ✗ Unable to find the iPhone Simulator SDK.
```

Note: I'm unsure of in the future if the SDK and the simulator runtime will need to match the exact version or just the major. For now, it only checks against the major version.

Part 3 of https://github.com/flutter/flutter/issues/129558.
2023-08-04 17:14:06 +00:00

132 lines
5.0 KiB
Dart

// Copyright 2014 The Flutter 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 '../base/user_messages.dart';
import '../base/version.dart';
import '../build_info.dart';
import '../doctor_validator.dart';
import '../ios/simulators.dart';
import 'xcode.dart';
String _iOSSimulatorMissing(String version) => '''
iOS $version Simulator not installed; this may be necessary for iOS and macOS development.
To download and install the platform, open Xcode, select Xcode > Settings > Platforms,
and click the GET button for the required platform.
For more information, please visit:
https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes''';
class XcodeValidator extends DoctorValidator {
XcodeValidator({
required Xcode xcode,
required IOSSimulatorUtils iosSimulatorUtils,
required UserMessages userMessages,
}) : _xcode = xcode,
_iosSimulatorUtils = iosSimulatorUtils,
_userMessages = userMessages,
super('Xcode - develop for iOS and macOS');
final Xcode _xcode;
final IOSSimulatorUtils _iosSimulatorUtils;
final UserMessages _userMessages;
@override
Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[];
ValidationType xcodeStatus = ValidationType.missing;
String? xcodeVersionInfo;
final String? xcodeSelectPath = _xcode.xcodeSelectPath;
if (_xcode.isInstalled) {
xcodeStatus = ValidationType.success;
if (xcodeSelectPath != null) {
messages.add(ValidationMessage(_userMessages.xcodeLocation(xcodeSelectPath)));
}
final String? versionText = _xcode.versionText;
if (versionText != null) {
xcodeVersionInfo = versionText;
if (xcodeVersionInfo.contains(',')) {
xcodeVersionInfo = xcodeVersionInfo.substring(0, xcodeVersionInfo.indexOf(','));
}
}
if (_xcode.buildVersion != null) {
messages.add(ValidationMessage('Build ${_xcode.buildVersion}'));
}
if (!_xcode.isInstalledAndMeetsVersionCheck) {
xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error(_userMessages.xcodeOutdated(xcodeRequiredVersion.toString())));
} else if (!_xcode.isRecommendedVersionSatisfactory) {
xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.hint(_userMessages.xcodeRecommended(xcodeRecommendedVersion.toString())));
}
if (!_xcode.eulaSigned) {
xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error(_userMessages.xcodeEula));
}
if (!_xcode.isSimctlInstalled) {
xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error(_userMessages.xcodeMissingSimct));
}
final ValidationMessage? missingSimulatorMessage = await _validateSimulatorRuntimeInstalled();
if (missingSimulatorMessage != null) {
xcodeStatus = ValidationType.partial;
messages.add(missingSimulatorMessage);
}
} else {
xcodeStatus = ValidationType.missing;
if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) {
messages.add(ValidationMessage.error(_userMessages.xcodeMissing));
} else {
messages.add(ValidationMessage.error(_userMessages.xcodeIncomplete));
}
}
return ValidationResult(xcodeStatus, messages, statusInfo: xcodeVersionInfo);
}
/// Validate the Xcode-installed iOS simulator SDK has a corresponding iOS
/// simulator runtime installed.
///
/// Starting with Xcode 15, the iOS simulator runtime is no longer downloaded
/// with Xcode and must be downloaded and installed separately.
/// iOS applications cannot be run without it.
Future<ValidationMessage?> _validateSimulatorRuntimeInstalled() async {
// Skip this validation if Xcode is not installed, Xcode is a version less
// than 15, simctl is not installed, or if the EULA is not signed.
if (!_xcode.isInstalled ||
_xcode.currentVersion == null ||
_xcode.currentVersion!.major < 15 ||
!_xcode.isSimctlInstalled ||
!_xcode.eulaSigned) {
return null;
}
final Version? platformSDKVersion = await _xcode.sdkPlatformVersion(EnvironmentType.simulator);
if (platformSDKVersion == null) {
return const ValidationMessage.error('Unable to find the iPhone Simulator SDK.');
}
final List<IOSSimulatorRuntime> runtimes = await _iosSimulatorUtils.getAvailableIOSRuntimes();
if (runtimes.isEmpty) {
return const ValidationMessage.error('Unable to get list of installed Simulator runtimes.');
}
// Verify there is a simulator runtime installed matching the
// iphonesimulator SDK major version.
try {
runtimes.firstWhere(
(IOSSimulatorRuntime runtime) =>
runtime.version?.major == platformSDKVersion.major,
);
} on StateError {
return ValidationMessage.hint(_iOSSimulatorMissing(platformSDKVersion.toString()));
}
return null;
}
}