Commit f8ab7265 authored by tonyzhao1's avatar tonyzhao1 Committed by Todd Volkert

Flutter doctor error message lookup (#23889)

parent 29bab6c0
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../base/user_messages.dart';
import '../base/version.dart'; import '../base/version.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../globals.dart'; import '../globals.dart';
...@@ -34,9 +35,9 @@ class AndroidStudioValidator extends DoctorValidator { ...@@ -34,9 +35,9 @@ class AndroidStudioValidator extends DoctorValidator {
final String studioVersionText = _studio.version == Version.unknown final String studioVersionText = _studio.version == Version.unknown
? null ? null
: 'version ${_studio.version}'; : userMessages.androidStudioVersion(_studio.version.toString());
messages messages
.add(ValidationMessage('Android Studio at ${_studio.directory}')); .add(ValidationMessage(userMessages.androidStudioLocation(_studio.directory)));
final IntelliJPlugins plugins = IntelliJPlugins(_studio.pluginsPath); final IntelliJPlugins plugins = IntelliJPlugins(_studio.pluginsPath);
plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'], plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
...@@ -51,11 +52,9 @@ class AndroidStudioValidator extends DoctorValidator { ...@@ -51,11 +52,9 @@ class AndroidStudioValidator extends DoctorValidator {
type = ValidationType.partial; type = ValidationType.partial;
messages.addAll(_studio.validationMessages messages.addAll(_studio.validationMessages
.map<ValidationMessage>((String m) => ValidationMessage.error(m))); .map<ValidationMessage>((String m) => ValidationMessage.error(m)));
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.androidStudioNeedsUpdate));
'Try updating or re-installing Android Studio.'));
if (_studio.configured != null) { if (_studio.configured != null) {
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.androidStudioResetDir));
'Consider removing your android-studio-dir setting by running:\nflutter config --android-studio-dir='));
} }
} }
...@@ -76,13 +75,9 @@ class NoAndroidStudioValidator extends DoctorValidator { ...@@ -76,13 +75,9 @@ class NoAndroidStudioValidator extends DoctorValidator {
final String cfgAndroidStudio = config.getValue('android-studio-dir'); final String cfgAndroidStudio = config.getValue('android-studio-dir');
if (cfgAndroidStudio != null) { if (cfgAndroidStudio != null) {
messages.add( messages.add(ValidationMessage.error(userMessages.androidStudioMissing(cfgAndroidStudio)));
ValidationMessage.error('android-studio-dir = $cfgAndroidStudio\n'
'but Android Studio not found at this location.'));
} }
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.androidStudioInstallation));
'Android Studio not found; download from https://developer.android.com/studio/index.html\n'
'(or visit https://flutter.io/setup/#android-setup for detailed instructions).'));
return ValidationResult(ValidationType.notAvailable, messages, return ValidationResult(ValidationType.notAvailable, messages,
statusInfo: 'not installed'); statusInfo: 'not installed');
......
...@@ -11,6 +11,7 @@ import '../base/io.dart'; ...@@ -11,6 +11,7 @@ import '../base/io.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../base/user_messages.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../base/version.dart'; import '../base/version.dart';
import '../doctor.dart'; import '../doctor.dart';
...@@ -49,13 +50,11 @@ class AndroidWorkflow implements Workflow { ...@@ -49,13 +50,11 @@ class AndroidWorkflow implements Workflow {
class AndroidValidator extends DoctorValidator { class AndroidValidator extends DoctorValidator {
AndroidValidator(): super('Android toolchain - develop for Android devices',); AndroidValidator(): super('Android toolchain - develop for Android devices',);
static const String _jdkDownload = 'https://www.oracle.com/technetwork/java/javase/downloads/';
/// Returns false if we cannot determine the Java version or if the version /// Returns false if we cannot determine the Java version or if the version
/// is not compatible. /// is not compatible.
Future<bool> _checkJavaVersion(String javaBinary, List<ValidationMessage> messages) async { Future<bool> _checkJavaVersion(String javaBinary, List<ValidationMessage> messages) async {
if (!processManager.canRun(javaBinary)) { if (!processManager.canRun(javaBinary)) {
messages.add(ValidationMessage.error('Cannot execute $javaBinary to determine the version')); messages.add(ValidationMessage.error(userMessages.androidCantRunJavaBinary(javaBinary)));
return false; return false;
} }
String javaVersion; String javaVersion;
...@@ -71,10 +70,10 @@ class AndroidValidator extends DoctorValidator { ...@@ -71,10 +70,10 @@ class AndroidValidator extends DoctorValidator {
} }
if (javaVersion == null) { if (javaVersion == null) {
// Could not determine the java version. // Could not determine the java version.
messages.add(ValidationMessage.error('Could not determine java version')); messages.add(ValidationMessage.error(userMessages.androidUnknownJavaVersion));
return false; return false;
} }
messages.add(ValidationMessage('Java version $javaVersion')); messages.add(ValidationMessage(userMessages.androidJavaVersion(javaVersion)));
// TODO(johnmccutchan): Validate version. // TODO(johnmccutchan): Validate version.
return true; return true;
} }
...@@ -87,38 +86,26 @@ class AndroidValidator extends DoctorValidator { ...@@ -87,38 +86,26 @@ class AndroidValidator extends DoctorValidator {
// No Android SDK found. // No Android SDK found.
if (platform.environment.containsKey(kAndroidHome)) { if (platform.environment.containsKey(kAndroidHome)) {
final String androidHomeDir = platform.environment[kAndroidHome]; final String androidHomeDir = platform.environment[kAndroidHome];
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.androidBadSdkDir(kAndroidHome, androidHomeDir)));
'$kAndroidHome = $androidHomeDir\n'
'but Android SDK not found at this location.'
));
} else { } else {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.androidMissingSdkInstructions(kAndroidHome)));
'Unable to locate Android SDK.\n'
'Install Android Studio from: https://developer.android.com/studio/index.html\n'
'On first launch it will assist you in installing the Android SDK components.\n'
'(or visit https://flutter.io/setup/#android-setup for detailed instructions).\n'
'If Android SDK has been installed to a custom location, set \$$kAndroidHome to that location.\n'
'You may also want to add it to your PATH environment variable.\n'
));
} }
return ValidationResult(ValidationType.missing, messages); return ValidationResult(ValidationType.missing, messages);
} }
messages.add(ValidationMessage('Android SDK at ${androidSdk.directory}')); messages.add(ValidationMessage(userMessages.androidSdkLocation(androidSdk.directory)));
messages.add(ValidationMessage(androidSdk.ndk == null messages.add(ValidationMessage(androidSdk.ndk == null
? 'Android NDK location not configured (optional; useful for native profiling support)' ? userMessages.androidMissingNdk
: 'Android NDK at ${androidSdk.ndk.directory}')); : userMessages.androidNdkLocation(androidSdk.ndk.directory)));
String sdkVersionText; String sdkVersionText;
if (androidSdk.latestVersion != null) { if (androidSdk.latestVersion != null) {
sdkVersionText = 'Android SDK ${androidSdk.latestVersion.buildToolsVersionName}'; sdkVersionText = userMessages.androidStatusInfo(androidSdk.latestVersion.buildToolsVersionName);
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.androidSdkPlatformToolsVersion(
'Platform ${androidSdk.latestVersion.platformName}, ' androidSdk.latestVersion.platformName,
'build-tools ${androidSdk.latestVersion.buildToolsVersionName}' androidSdk.latestVersion.buildToolsVersionName)));
));
} }
if (platform.environment.containsKey(kAndroidHome)) { if (platform.environment.containsKey(kAndroidHome)) {
...@@ -137,22 +124,17 @@ class AndroidValidator extends DoctorValidator { ...@@ -137,22 +124,17 @@ class AndroidValidator extends DoctorValidator {
messages.addAll(validationResult.map<ValidationMessage>((String message) { messages.addAll(validationResult.map<ValidationMessage>((String message) {
return ValidationMessage.error(message); return ValidationMessage.error(message);
})); }));
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.androidSdkInstallHelp));
'Try re-installing or updating your Android SDK,\n'
'visit https://flutter.io/setup/#android-setup for detailed instructions.'));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
} }
// Now check for the JDK. // Now check for the JDK.
final String javaBinary = AndroidSdk.findJavaBinary(); final String javaBinary = AndroidSdk.findJavaBinary();
if (javaBinary == null) { if (javaBinary == null) {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.androidMissingJdk));
'No Java Development Kit (JDK) found; You must have the environment '
'variable JAVA_HOME set and the java binary in your PATH. '
'You can download the JDK from $_jdkDownload.'));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
} }
messages.add(ValidationMessage('Java binary at: $javaBinary')); messages.add(ValidationMessage(userMessages.androidJdkLocation(javaBinary)));
// Check JDK version. // Check JDK version.
if (! await _checkJavaVersion(javaBinary, messages)) { if (! await _checkJavaVersion(javaBinary, messages)) {
...@@ -178,21 +160,21 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -178,21 +160,21 @@ class AndroidLicenseValidator extends DoctorValidator {
return ValidationResult(ValidationType.missing, messages); return ValidationResult(ValidationType.missing, messages);
} }
final String sdkVersionText = 'Android SDK ${androidSdk.latestVersion.buildToolsVersionName}'; final String sdkVersionText = userMessages.androidStatusInfo(androidSdk.latestVersion.buildToolsVersionName);
// Check for licenses. // Check for licenses.
switch (await licensesAccepted) { switch (await licensesAccepted) {
case LicensesAccepted.all: case LicensesAccepted.all:
messages.add(ValidationMessage('All Android licenses accepted.')); messages.add(ValidationMessage(userMessages.androidLicensesAll));
break; break;
case LicensesAccepted.some: case LicensesAccepted.some:
messages.add(ValidationMessage.hint('Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses')); messages.add(ValidationMessage.hint(userMessages.androidLicensesSome));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
case LicensesAccepted.none: case LicensesAccepted.none:
messages.add(ValidationMessage.error('Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses')); messages.add(ValidationMessage.error(userMessages.androidLicensesNone));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
case LicensesAccepted.unknown: case LicensesAccepted.unknown:
messages.add(ValidationMessage.error('Android license status unknown.')); messages.add(ValidationMessage.error(userMessages.androidLicensesUnknown));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
} }
return ValidationResult(ValidationType.installed, messages, statusInfo: sdkVersionText); return ValidationResult(ValidationType.installed, messages, statusInfo: sdkVersionText);
...@@ -263,7 +245,7 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -263,7 +245,7 @@ class AndroidLicenseValidator extends DoctorValidator {
try { try {
await Future.wait<void>(<Future<void>>[output, errors]).timeout(const Duration(seconds: 30)); await Future.wait<void>(<Future<void>>[output, errors]).timeout(const Duration(seconds: 30));
} catch (TimeoutException) { } catch (TimeoutException) {
printTrace('Intentionally killing ${androidSdk.sdkManagerPath}'); printTrace(userMessages.androidLicensesTimeout(androidSdk.sdkManagerPath));
processManager.killPid(process.pid); processManager.killPid(process.pid);
} }
return status ?? LicensesAccepted.unknown; return status ?? LicensesAccepted.unknown;
...@@ -272,7 +254,7 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -272,7 +254,7 @@ class AndroidLicenseValidator extends DoctorValidator {
/// Run the Android SDK manager tool in order to accept SDK licenses. /// Run the Android SDK manager tool in order to accept SDK licenses.
static Future<bool> runLicenseManager() async { static Future<bool> runLicenseManager() async {
if (androidSdk == null) { if (androidSdk == null) {
printStatus('Unable to locate Android SDK.'); printStatus(userMessages.androidSdkShort);
return false; return false;
} }
...@@ -281,10 +263,7 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -281,10 +263,7 @@ class AndroidLicenseValidator extends DoctorValidator {
final Version sdkManagerVersion = Version.parse(androidSdk.sdkManagerVersion); final Version sdkManagerVersion = Version.parse(androidSdk.sdkManagerVersion);
if (sdkManagerVersion == null || sdkManagerVersion.major < 26) if (sdkManagerVersion == null || sdkManagerVersion.major < 26)
// SDK manager is found, but needs to be updated. // SDK manager is found, but needs to be updated.
throwToolExit( throwToolExit(userMessages.androidSdkOutdated(androidSdk.sdkManagerPath));
'A newer version of the Android SDK is required. To update, run:\n'
'${androidSdk.sdkManagerPath} --update\n'
);
final Process process = await runCommand( final Process process = await runCommand(
<String>[androidSdk.sdkManagerPath, '--licenses'], <String>[androidSdk.sdkManagerPath, '--licenses'],
...@@ -309,10 +288,6 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -309,10 +288,6 @@ class AndroidLicenseValidator extends DoctorValidator {
assert(androidSdk != null); assert(androidSdk != null);
final String sdkManagerPath = androidSdk.sdkManagerPath; final String sdkManagerPath = androidSdk.sdkManagerPath;
if (!processManager.canRun(sdkManagerPath)) if (!processManager.canRun(sdkManagerPath))
throwToolExit( throwToolExit(userMessages.androidMissingSdkManager(sdkManagerPath));
'Android sdkmanager tool not found ($sdkManagerPath).\n'
'Try re-installing or updating your Android SDK,\n'
'visit https://flutter.io/setup/#android-setup for detailed instructions.'
);
} }
} }
import 'context.dart';
UserMessages get userMessages => context[UserMessages];
/// Class containing message strings that can be produced by Flutter tools.
class UserMessages {
// Messages used in FlutterValidator
String flutterStatusInfo(String channel, String version, String os, String locale) =>
'Channel $channel, v$version, on $os, locale $locale';
String flutterVersion(String version, String flutterRoot) =>
'Flutter version $version at $flutterRoot';
String flutterRevision(String revision, String age, String date) =>
'Framework revision $revision ($age), $date';
String engineRevision(String revision) => 'Engine revision $revision';
String dartRevision(String revision) => 'Dart version $revision';
String get flutterBinariesDoNotRun =>
'Downloaded executables cannot execute on host.\n'
'See https://github.com/flutter/flutter/issues/6207 for more information';
String get flutterBinariesLinuxRepairCommands =>
'On Debian/Ubuntu/Mint: sudo apt-get install lib32stdc++6\n'
'On Fedora: dnf install libstdc++.i686\n'
'On Arch: pacman -S lib32-libstdc++5';
// Messages used in NoIdeValidator
String get noIdeStatusInfo => 'No supported IDEs installed';
String get noIdeInstallationInfo => 'IntelliJ - https://www.jetbrains.com/idea/';
// Messages used in IntellijValidator
String intellijStatusInfo(String version) => 'version $version';
String get intellijPluginInfo =>
'For information about installing plugins, see\n'
'https://flutter.io/intellij-setup/#installing-the-plugins';
String intellijMinimumVersion(String minVersion) =>
'This install is older than the minimum recommended version of $minVersion.';
String intellijLocation(String installPath) => 'IntelliJ at $installPath';
// Message used in IntellijValidatorOnMac
String get intellijMacUnknownResult => 'Cannot determine if IntelliJ is installed';
// Messages used in DeviceValidator
String get devicesMissing => 'No devices available';
String devicesAvailable(int devices) => '$devices available';
// Messages used in AndroidValidator
String androidCantRunJavaBinary(String javaBinary) => 'Cannot execute $javaBinary to determine the version';
String get androidUnknownJavaVersion => 'Could not determine java version';
String androidJavaVersion(String javaVersion) => 'Java version $javaVersion';
String androidBadSdkDir(String envKey, String homeDir) =>
'$envKey = $homeDir\n'
'but Android SDK not found at this location.';
String androidMissingSdkInstructions(String envKey) =>
'Unable to locate Android SDK.\n'
'Install Android Studio from: https://developer.android.com/studio/index.html\n'
'On first launch it will assist you in installing the Android SDK components.\n'
'(or visit https://flutter.io/setup/#android-setup for detailed instructions).\n'
'If Android SDK has been installed to a custom location, set $envKey to that location.\n'
'You may also want to add it to your PATH environment variable.\n';
String androidSdkLocation(String directory) => 'Android SDK at $directory';
String androidSdkPlatformToolsVersion(String platform, String tools) =>
'Platform $platform, build-tools $tools';
String get androidSdkInstallHelp =>
'Try re-installing or updating your Android SDK,\n'
'visit https://flutter.io/setup/#android-setup for detailed instructions.';
String get androidMissingNdk => 'Android NDK location not configured (optional; useful for native profiling support)';
String androidNdkLocation(String directory) => 'Android NDK at $directory';
// Also occurs in AndroidLicenseValidator
String androidStatusInfo(String version) => 'Android SDK version $version';
// Messages used in AndroidLicenseValidator
String get androidMissingJdk =>
'No Java Development Kit (JDK) found; You must have the environment '
'variable JAVA_HOME set and the java binary in your PATH. '
'You can download the JDK from https://www.oracle.com/technetwork/java/javase/downloads/.';
String androidJdkLocation(String location) => 'Java binary at: $location';
String get androidLicensesAll => 'All Android licenses accepted.';
String get androidLicensesSome => 'Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses';
String get androidLicensesNone => 'Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses';
String get androidLicensesUnknown => 'Android license status unknown.';
String androidSdkOutdated(String managerPath) =>
'A newer version of the Android SDK is required. To update, run:\n'
'$managerPath --update\n';
String androidLicensesTimeout(String managerPath) => 'Intentionally killing $managerPath';
String get androidSdkShort => 'Unable to locate Android SDK.';
String androidMissingSdkManager(String sdkManagerPath) =>
'Android sdkmanager tool not found ($sdkManagerPath).\n'
'Try re-installing or updating your Android SDK,\n'
'visit https://flutter.io/setup/#android-setup for detailed instructions.';
// Messages used in AndroidStudioValidator
String androidStudioVersion(String version) => 'version $version';
String androidStudioLocation(String location) => 'Android Studio at $location';
String get androidStudioNeedsUpdate => 'Try updating or re-installing Android Studio.';
String get androidStudioResetDir =>
'Consider removing your android-studio-dir setting by running:\n'
'flutter config --android-studio-dir=';
// Messages used in NoAndroidStudioValidator
String androidStudioMissing(String location) =>
'android-studio-dir = $location\n'
'but Android Studio not found at this location.';
String get androidStudioInstallation =>
'Android Studio not found; download from https://developer.android.com/studio/index.html\n'
'(or visit https://flutter.io/setup/#android-setup for detailed instructions).';
// Messages used in IOSValidator
String iOSXcodeLocation(String location) => 'Xcode at $location';
String iOSXcodeOutdated(int versionMajor, int versionMinor) =>
'Flutter requires a minimum Xcode version of $versionMajor.$versionMinor.0.\n'
'Download the latest version or update via the Mac App Store.';
String get iOSXcodeEula => 'Xcode end user license agreement not signed; open Xcode or run the command \'sudo xcodebuild -license\'.';
String get iOSXcodeMissingSimct =>
'Xcode requires additional components to be installed in order to run.\n'
'Launch Xcode and install additional required components when prompted.';
String get iOSXcodeMissing =>
'Xcode not installed; this is necessary for iOS development.\n'
'Download at https://developer.apple.com/xcode/download/.';
String get iOSXcodeIncomplete =>
'Xcode installation is incomplete; a full installation is necessary for iOS development.\n'
'Download at: https://developer.apple.com/xcode/download/\n'
'Or install Xcode via the App Store.\n'
'Once installed, run:\n'
' sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer';
String get iOSIMobileDeviceMissing =>
'libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:\n'
' brew update\n'
' brew install --HEAD usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller';
String get iOSIMobileDeviceBroken =>
'Verify that all connected devices have been paired with this computer in Xcode.\n'
'If all devices have been paired, libimobiledevice and ideviceinstaller may require updating.\n'
'To update with Brew, run:\n'
' brew update\n'
' brew uninstall --ignore-dependencies libimobiledevice\n'
' brew uninstall --ignore-dependencies usbmuxd\n'
' brew install --HEAD usbmuxd\n'
' brew unlink usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller';
String get iOSDeviceInstallerMissing =>
'ideviceinstaller is not installed; this is used to discover connected iOS devices.\n'
'To install with Brew, run:\n'
' brew install --HEAD usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller';
String iOSDeployVersion(String version) => 'ios-deploy $version';
String iOSDeployOutdated(String minVersion) =>
'ios-deploy out of date ($minVersion is required). To upgrade with Brew:\n'
' brew upgrade ios-deploy';
String get iOSDeployMissing =>
'ios-deploy not installed. To install:\n'
' brew install ios-deploy';
String get iOSBrewMissing =>
'Brew can be used to install tools for iOS device development.\n'
'Download brew at https://brew.sh/.';
// Messages used in CocoaPodsValidator
String cocoaPodsVersion(String version) => 'CocoaPods version $version';
String cocoaPodsUninitialized(String consequence) =>
'CocoaPods installed but not initialized.\n'
'$consequence\n'
'To initialize CocoaPods, run:\n'
' pod setup\n'
'once to finalize CocoaPods\' installation.';
String cocoaPodsMissing(String consequence, String installInstructions) =>
'CocoaPods not installed.\n'
'$consequence\n'
'To install:\n'
'$installInstructions';
String cocoaPodsOutdated(String recVersion, String consequence, String upgradeInstructions) =>
'CocoaPods out of date ($recVersion is recommended).\n'
'$consequence\n'
'To upgrade:\n'
'$upgradeInstructions';
// Messages used in VsCodeValidator
String vsCodeVersion(String version) => 'version $version';
String vsCodeLocation(String location) => 'VS Code at $location';
String vsCodeFlutterExtensionMissing(String url) => 'Flutter extension not installed; install from\n$url';
}
...@@ -18,6 +18,7 @@ import 'base/logger.dart'; ...@@ -18,6 +18,7 @@ import 'base/logger.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/time.dart'; import 'base/time.dart';
import 'base/user_messages.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'cache.dart'; import 'cache.dart';
import 'compile.dart'; import 'compile.dart';
...@@ -81,6 +82,7 @@ Future<T> runInContext<T>( ...@@ -81,6 +82,7 @@ Future<T> runInContext<T>(
SystemClock: () => const SystemClock(), SystemClock: () => const SystemClock(),
Stdio: () => const Stdio(), Stdio: () => const Stdio(),
Usage: () => Usage(), Usage: () => Usage(),
UserMessages: () => UserMessages(),
Xcode: () => Xcode(), Xcode: () => Xcode(),
XcodeProjectInterpreter: () => XcodeProjectInterpreter(), XcodeProjectInterpreter: () => XcodeProjectInterpreter(),
}, },
......
...@@ -15,6 +15,7 @@ import 'base/os.dart'; ...@@ -15,6 +15,7 @@ import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/process_manager.dart'; import 'base/process_manager.dart';
import 'base/terminal.dart'; import 'base/terminal.dart';
import 'base/user_messages.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'base/version.dart'; import 'base/version.dart';
import 'cache.dart'; import 'cache.dart';
...@@ -432,32 +433,26 @@ class _FlutterValidator extends DoctorValidator { ...@@ -432,32 +433,26 @@ class _FlutterValidator extends DoctorValidator {
final FlutterVersion version = FlutterVersion.instance; final FlutterVersion version = FlutterVersion.instance;
messages.add(ValidationMessage('Flutter version ${version.frameworkVersion} at ${Cache.flutterRoot}')); messages.add(ValidationMessage(userMessages.flutterVersion(version.frameworkVersion, Cache.flutterRoot)));
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.flutterRevision(version.frameworkRevisionShort, version.frameworkAge, version.frameworkDate)));
'Framework revision ${version.frameworkRevisionShort} ' messages.add(ValidationMessage(userMessages.engineRevision(version.engineRevisionShort)));
'(${version.frameworkAge}), ${version.frameworkDate}' messages.add(ValidationMessage(userMessages.dartRevision(version.dartSdkVersion)));
));
messages.add(ValidationMessage('Engine revision ${version.engineRevisionShort}'));
messages.add(ValidationMessage('Dart version ${version.dartSdkVersion}'));
final String genSnapshotPath = final String genSnapshotPath =
artifacts.getArtifactPath(Artifact.genSnapshot); artifacts.getArtifactPath(Artifact.genSnapshot);
// Check that the binaries we downloaded for this platform actually run on it. // Check that the binaries we downloaded for this platform actually run on it.
if (!_genSnapshotRuns(genSnapshotPath)) { if (!_genSnapshotRuns(genSnapshotPath)) {
final StringBuffer buf = StringBuffer(); final StringBuffer buf = StringBuffer();
buf.writeln('Downloaded executables cannot execute on host.'); buf.writeln(userMessages.flutterBinariesDoNotRun);
buf.writeln('See https://github.com/flutter/flutter/issues/6207 for more information');
if (platform.isLinux) { if (platform.isLinux) {
buf.writeln('On Debian/Ubuntu/Mint: sudo apt-get install lib32stdc++6'); buf.writeln(userMessages.flutterBinariesLinuxRepairCommands);
buf.writeln('On Fedora: dnf install libstdc++.i686');
buf.writeln('On Arch: pacman -S lib32-libstdc++5');
} }
messages.add(ValidationMessage.error(buf.toString())); messages.add(ValidationMessage.error(buf.toString()));
valid = ValidationType.partial; valid = ValidationType.partial;
} }
return ValidationResult(valid, messages, return ValidationResult(valid, messages,
statusInfo: 'Channel ${version.channel}, v${version.frameworkVersion}, on ${os.name}, locale ${platform.localeName}' statusInfo: userMessages.flutterStatusInfo(version.channel, version.frameworkVersion, os.name, platform.localeName)
); );
} }
} }
...@@ -477,8 +472,8 @@ class NoIdeValidator extends DoctorValidator { ...@@ -477,8 +472,8 @@ class NoIdeValidator extends DoctorValidator {
@override @override
Future<ValidationResult> validate() async { Future<ValidationResult> validate() async {
return ValidationResult(ValidationType.missing, <ValidationMessage>[ return ValidationResult(ValidationType.missing, <ValidationMessage>[
ValidationMessage('IntelliJ - https://www.jetbrains.com/idea/'), ValidationMessage(userMessages.noIdeInstallationInfo),
], statusInfo: 'No supported IDEs installed'); ], statusInfo: userMessages.noIdeStatusInfo);
} }
} }
...@@ -509,7 +504,7 @@ abstract class IntelliJValidator extends DoctorValidator { ...@@ -509,7 +504,7 @@ abstract class IntelliJValidator extends DoctorValidator {
Future<ValidationResult> validate() async { Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[]; final List<ValidationMessage> messages = <ValidationMessage>[];
messages.add(ValidationMessage('IntelliJ at $installPath')); messages.add(ValidationMessage(userMessages.intellijLocation(installPath)));
final IntelliJPlugins plugins = IntelliJPlugins(pluginsPath); final IntelliJPlugins plugins = IntelliJPlugins(pluginsPath);
plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'], plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
...@@ -517,10 +512,7 @@ abstract class IntelliJValidator extends DoctorValidator { ...@@ -517,10 +512,7 @@ abstract class IntelliJValidator extends DoctorValidator {
plugins.validatePackage(messages, <String>['Dart'], 'Dart'); plugins.validatePackage(messages, <String>['Dart'], 'Dart');
if (_hasIssues(messages)) { if (_hasIssues(messages)) {
messages.add(ValidationMessage( messages.add(ValidationMessage(userMessages.intellijPluginInfo));
'For information about installing plugins, see\n'
'https://flutter.io/intellij-setup/#installing-the-plugins'
));
} }
_validateIntelliJVersion(messages, kMinIdeaVersion); _validateIntelliJVersion(messages, kMinIdeaVersion);
...@@ -528,8 +520,7 @@ abstract class IntelliJValidator extends DoctorValidator { ...@@ -528,8 +520,7 @@ abstract class IntelliJValidator extends DoctorValidator {
return ValidationResult( return ValidationResult(
_hasIssues(messages) ? ValidationType.partial : ValidationType.installed, _hasIssues(messages) ? ValidationType.partial : ValidationType.installed,
messages, messages,
statusInfo: 'version $version' statusInfo: userMessages.intellijStatusInfo(version));
);
} }
bool _hasIssues(List<ValidationMessage> messages) { bool _hasIssues(List<ValidationMessage> messages) {
...@@ -546,9 +537,7 @@ abstract class IntelliJValidator extends DoctorValidator { ...@@ -546,9 +537,7 @@ abstract class IntelliJValidator extends DoctorValidator {
return; return;
if (installedVersion < minVersion) { if (installedVersion < minVersion) {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.intellijMinimumVersion(minVersion.toString())));
'This install is older than the minimum recommended version of $minVersion.'
));
} }
} }
} }
...@@ -648,7 +637,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator { ...@@ -648,7 +637,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
} }
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
validators.add(ValidatorWithResult( validators.add(ValidatorWithResult(
'Cannot determine if IntelliJ is installed', userMessages.intellijMacUnknownResult,
ValidationResult(ValidationType.missing, <ValidationMessage>[ ValidationResult(ValidationType.missing, <ValidationMessage>[
ValidationMessage.error(e.message), ValidationMessage.error(e.message),
]), ]),
...@@ -691,7 +680,7 @@ class DeviceValidator extends DoctorValidator { ...@@ -691,7 +680,7 @@ class DeviceValidator extends DoctorValidator {
if (diagnostics.isNotEmpty) { if (diagnostics.isNotEmpty) {
messages = diagnostics.map<ValidationMessage>((String message) => ValidationMessage(message)).toList(); messages = diagnostics.map<ValidationMessage>((String message) => ValidationMessage(message)).toList();
} else { } else {
messages = <ValidationMessage>[ValidationMessage.hint('No devices available')]; messages = <ValidationMessage>[ValidationMessage.hint(userMessages.devicesMissing)];
} }
} else { } else {
messages = await Device.descriptions(devices) messages = await Device.descriptions(devices)
...@@ -701,7 +690,7 @@ class DeviceValidator extends DoctorValidator { ...@@ -701,7 +690,7 @@ class DeviceValidator extends DoctorValidator {
if (devices.isEmpty) { if (devices.isEmpty) {
return ValidationResult(ValidationType.notAvailable, messages); return ValidationResult(ValidationType.notAvailable, messages);
} else { } else {
return ValidationResult(ValidationType.installed, messages, statusInfo: '${devices.length} available'); return ValidationResult(ValidationType.installed, messages, statusInfo: userMessages.devicesAvailable(devices.length));
} }
} }
} }
......
...@@ -8,6 +8,7 @@ import '../base/context.dart'; ...@@ -8,6 +8,7 @@ import '../base/context.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';
import '../base/user_messages.dart';
import '../base/version.dart'; import '../base/version.dart';
import '../doctor.dart'; import '../doctor.dart';
import 'cocoapods.dart'; import 'cocoapods.dart';
...@@ -81,7 +82,7 @@ class IOSValidator extends DoctorValidator { ...@@ -81,7 +82,7 @@ class IOSValidator extends DoctorValidator {
if (xcode.isInstalled) { if (xcode.isInstalled) {
xcodeStatus = ValidationType.installed; xcodeStatus = ValidationType.installed;
messages.add(ValidationMessage('Xcode at ${xcode.xcodeSelectPath}')); messages.add(ValidationMessage(userMessages.iOSXcodeLocation(xcode.xcodeSelectPath)));
xcodeVersionInfo = xcode.versionText; xcodeVersionInfo = xcode.versionText;
if (xcodeVersionInfo.contains(',')) if (xcodeVersionInfo.contains(','))
...@@ -91,40 +92,25 @@ class IOSValidator extends DoctorValidator { ...@@ -91,40 +92,25 @@ class IOSValidator extends DoctorValidator {
if (!xcode.isInstalledAndMeetsVersionCheck) { if (!xcode.isInstalledAndMeetsVersionCheck) {
xcodeStatus = ValidationType.partial; xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
'Flutter requires a minimum Xcode version of $kXcodeRequiredVersionMajor.$kXcodeRequiredVersionMinor.0.\n' userMessages.iOSXcodeOutdated(kXcodeRequiredVersionMajor, kXcodeRequiredVersionMinor)
'Download the latest version or update via the Mac App Store.'
)); ));
} }
if (!xcode.eulaSigned) { if (!xcode.eulaSigned) {
xcodeStatus = ValidationType.partial; xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSXcodeEula));
'Xcode end user license agreement not signed; open Xcode or run the command \'sudo xcodebuild -license\'.'
));
} }
if (!xcode.isSimctlInstalled) { if (!xcode.isSimctlInstalled) {
xcodeStatus = ValidationType.partial; xcodeStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSXcodeMissingSimct));
'Xcode requires additional components to be installed in order to run.\n'
'Launch Xcode and install additional required components when prompted.'
));
} }
} else { } else {
xcodeStatus = ValidationType.missing; xcodeStatus = ValidationType.missing;
if (xcode.xcodeSelectPath == null || xcode.xcodeSelectPath.isEmpty) { if (xcode.xcodeSelectPath == null || xcode.xcodeSelectPath.isEmpty) {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSXcodeMissing));
'Xcode not installed; this is necessary for iOS development.\n'
'Download at https://developer.apple.com/xcode/download/.'
));
} else { } else {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSXcodeIncomplete));
'Xcode installation is incomplete; a full installation is necessary for iOS development.\n'
'Download at: https://developer.apple.com/xcode/download/\n'
'Or install Xcode via the App Store.\n'
'Once installed, run:\n'
' sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer'
));
} }
} }
...@@ -133,63 +119,30 @@ class IOSValidator extends DoctorValidator { ...@@ -133,63 +119,30 @@ class IOSValidator extends DoctorValidator {
if (!iMobileDevice.isInstalled) { if (!iMobileDevice.isInstalled) {
checksFailed += 3; checksFailed += 3;
packageManagerStatus = ValidationType.partial; packageManagerStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSIMobileDeviceMissing));
'libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:\n'
' brew update\n'
' brew install --HEAD usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller'
));
} else if (!await iMobileDevice.isWorking) { } else if (!await iMobileDevice.isWorking) {
checksFailed += 2; checksFailed += 2;
packageManagerStatus = ValidationType.partial; packageManagerStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSIMobileDeviceBroken));
'Verify that all connected devices have been paired with this computer in Xcode.\n'
'If all devices have been paired, libimobiledevice and ideviceinstaller may require updating.\n'
'To update with Brew, run:\n'
' brew update\n'
' brew uninstall --ignore-dependencies libimobiledevice\n'
' brew uninstall --ignore-dependencies usbmuxd\n'
' brew install --HEAD usbmuxd\n'
' brew unlink usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller'
));
} else if (!await hasIDeviceInstaller) { } else if (!await hasIDeviceInstaller) {
checksFailed += 1; checksFailed += 1;
packageManagerStatus = ValidationType.partial; packageManagerStatus = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSDeviceInstallerMissing));
'ideviceinstaller is not installed; this is used to discover connected iOS devices.\n'
'To install with Brew, run:\n'
' brew install --HEAD usbmuxd\n'
' brew link usbmuxd\n'
' brew install --HEAD libimobiledevice\n'
' brew install ideviceinstaller'
));
} }
final bool iHasIosDeploy = await hasIosDeploy; final bool iHasIosDeploy = await hasIosDeploy;
// Check ios-deploy is installed at meets version requirements. // Check ios-deploy is installed at meets version requirements.
if (iHasIosDeploy) { if (iHasIosDeploy) {
messages.add( messages.add(ValidationMessage(userMessages.iOSDeployVersion(await iosDeployVersionText)));
ValidationMessage('ios-deploy ${await iosDeployVersionText}'));
} }
if (!await _iosDeployIsInstalledAndMeetsVersionCheck) { if (!await _iosDeployIsInstalledAndMeetsVersionCheck) {
packageManagerStatus = ValidationType.partial; packageManagerStatus = ValidationType.partial;
if (iHasIosDeploy) { if (iHasIosDeploy) {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSDeployOutdated(iosDeployMinimumVersion)));
'ios-deploy out of date ($iosDeployMinimumVersion is required). To upgrade with Brew:\n'
' brew upgrade ios-deploy'
));
} else { } else {
checksFailed += 1; checksFailed += 1;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSDeployMissing));
'ios-deploy not installed. To install with Brew:\n'
' brew install ios-deploy'
));
} }
} }
...@@ -198,10 +151,7 @@ class IOSValidator extends DoctorValidator { ...@@ -198,10 +151,7 @@ class IOSValidator extends DoctorValidator {
if (checksFailed == totalChecks) if (checksFailed == totalChecks)
packageManagerStatus = ValidationType.missing; packageManagerStatus = ValidationType.missing;
if (checksFailed > 0 && !hasHomebrew) { if (checksFailed > 0 && !hasHomebrew) {
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.iOSBrewMissing));
'Brew can be used to install tools for iOS device development.\n'
'Download brew at https://brew.sh/.'
));
} }
return ValidationResult( return ValidationResult(
...@@ -232,34 +182,20 @@ class CocoaPodsValidator extends DoctorValidator { ...@@ -232,34 +182,20 @@ class CocoaPodsValidator extends DoctorValidator {
if (cocoaPodsStatus == CocoaPodsStatus.recommended) { if (cocoaPodsStatus == CocoaPodsStatus.recommended) {
if (await cocoaPods.isCocoaPodsInitialized) { if (await cocoaPods.isCocoaPodsInitialized) {
messages.add(ValidationMessage('CocoaPods version ${await cocoaPods.cocoaPodsVersionText}')); messages.add(ValidationMessage(userMessages.cocoaPodsVersion(await cocoaPods.cocoaPodsVersionText)));
} else { } else {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(userMessages.cocoaPodsUninitialized(noCocoaPodsConsequence)));
'CocoaPods installed but not initialized.\n'
'$noCocoaPodsConsequence\n'
'To initialize CocoaPods, run:\n'
' pod setup\n'
'once to finalize CocoaPods\' installation.'
));
} }
} else { } else {
if (cocoaPodsStatus == CocoaPodsStatus.notInstalled) { if (cocoaPodsStatus == CocoaPodsStatus.notInstalled) {
status = ValidationType.missing; status = ValidationType.missing;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
'CocoaPods not installed.\n' userMessages.cocoaPodsMissing(noCocoaPodsConsequence, cocoaPodsInstallInstructions)));
'$noCocoaPodsConsequence\n'
'To install:\n'
'$cocoaPodsInstallInstructions'
));
} else { } else {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.hint( messages.add(ValidationMessage.hint(
'CocoaPods out of date (${cocoaPods.cocoaPodsRecommendedVersion} is recommended).\n' userMessages.cocoaPodsOutdated(cocoaPods.cocoaPodsRecommendedVersion, noCocoaPodsConsequence, cocoaPodsUpgradeInstructions)));
'$noCocoaPodsConsequence\n'
'To upgrade:\n'
'$cocoaPodsUpgradeInstructions'
));
} }
} }
} else { } else {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../base/user_messages.dart';
import '../base/version.dart'; import '../base/version.dart';
import '../doctor.dart'; import '../doctor.dart';
import 'vscode.dart'; import 'vscode.dart';
...@@ -24,7 +25,7 @@ class VsCodeValidator extends DoctorValidator { ...@@ -24,7 +25,7 @@ class VsCodeValidator extends DoctorValidator {
Future<ValidationResult> validate() async { Future<ValidationResult> validate() async {
final String vsCodeVersionText = _vsCode.version == Version.unknown final String vsCodeVersionText = _vsCode.version == Version.unknown
? null ? null
: 'version ${_vsCode.version}'; : userMessages.vsCodeVersion(_vsCode.version.toString());
final ValidationType validationType = _vsCode.isValid final ValidationType validationType = _vsCode.isValid
? ValidationType.installed ? ValidationType.installed
......
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