blob: 3d82ab5b06621faa6c35e4de8ff3a9fc12189bd8 [file] [log] [blame]
// 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:convert' show JSON;
import 'dart:io';
import 'package:path/path.dart' as path;
import 'android/android_workflow.dart';
import 'base/context.dart';
import 'base/os.dart';
import 'globals.dart';
import 'ios/ios_workflow.dart';
import 'runner/version.dart';
const Map<String, String> _osNames = const <String, String>{
'macos': 'Mac OS',
'linux': 'Linux',
'windows': 'Windows'
};
String osName() {
String os = Platform.operatingSystem;
return _osNames.containsKey(os) ? _osNames[os] : os;
}
class Doctor {
Doctor() {
_validators.add(new _FlutterValidator());
_androidWorkflow = new AndroidWorkflow();
if (_androidWorkflow.appliesToHostPlatform)
_validators.add(_androidWorkflow);
_iosWorkflow = new IOSWorkflow();
if (_iosWorkflow.appliesToHostPlatform)
_validators.add(_iosWorkflow);
_validators.add(new _AtomValidator());
}
static void initGlobal() {
context[Doctor] = new Doctor();
}
IOSWorkflow _iosWorkflow;
AndroidWorkflow _androidWorkflow;
/// This can return null for platforms that don't support developing for iOS.
IOSWorkflow get iosWorkflow => _iosWorkflow;
AndroidWorkflow get androidWorkflow => _androidWorkflow;
List<DoctorValidator> _validators = <DoctorValidator>[];
List<Workflow> get workflows {
return new List<Workflow>.from(_validators.where((DoctorValidator validator) => validator is Workflow));
}
/// Print a summary of the state of the tooling, as well as how to get more info.
void summary() => printStatus(summaryText);
String get summaryText {
StringBuffer buffer = new StringBuffer();
bool allGood = true;
for (DoctorValidator validator in _validators) {
ValidationResult result = validator.validate();
buffer.write('${result.leadingBox} ${validator.title} is ');
if (result.type == ValidationType.missing)
buffer.write('not installed.');
else if (result.type == ValidationType.partial)
buffer.write('partially installed; more components are available.');
else
buffer.write('fully installed.');
if (result.statusInfo != null)
buffer.write(' (${result.statusInfo})');
buffer.writeln();
if (result.type != ValidationType.installed)
allGood = false;
}
if (!allGood) {
buffer.writeln();
buffer.write('Run "flutter doctor" for information about installing additional components.');
}
return buffer.toString();
}
/// Print verbose information about the state of installed tooling.
void diagnose() {
bool firstLine = true;
for (DoctorValidator validator in _validators) {
if (!firstLine)
printStatus('');
firstLine = false;
ValidationResult result = validator.validate();
if (result.statusInfo != null)
printStatus('${result.leadingBox} ${validator.title} (${result.statusInfo})');
else
printStatus('${result.leadingBox} ${validator.title}');
for (ValidationMessage message in result.messages) {
if (message.isError) {
printStatus(' x ${message.message.replaceAll('\n', '\n ')}');
} else {
printStatus(' • ${message.message.replaceAll('\n', '\n ')}');
}
}
}
}
bool get canListAnything => workflows.any((Workflow workflow) => workflow.canListDevices);
bool get canLaunchAnything => workflows.any((Workflow workflow) => workflow.canLaunchDevices);
}
/// A series of tools and required install steps for a target platform (iOS or Android).
abstract class Workflow {
/// Whether the workflow applies to this platform (as in, should we ever try and use it).
bool get appliesToHostPlatform;
/// Are we functional enough to list devices?
bool get canListDevices;
/// Could this thing launch *something*? It may still have minor issues.
bool get canLaunchDevices;
}
enum ValidationType {
missing,
partial,
installed
}
abstract class DoctorValidator {
DoctorValidator(this.title);
final String title;
ValidationResult validate();
}
class ValidationResult {
ValidationResult(this.type, this.messages, { this.statusInfo });
final ValidationType type;
// A short message about the status.
final String statusInfo;
final List<ValidationMessage> messages;
String get leadingBox {
if (type == ValidationType.missing)
return '[ ]';
else if (type == ValidationType.installed)
return '[✓]';
else
return '[-]';
}
}
class ValidationMessage {
ValidationMessage(this.message) : isError = false;
ValidationMessage.error(this.message) : isError = true;
final bool isError;
final String message;
@override
String toString() => message;
}
class _FlutterValidator extends DoctorValidator {
_FlutterValidator() : super('Flutter');
@override
ValidationResult validate() {
List<ValidationMessage> messages = <ValidationMessage>[];
FlutterVersion version = FlutterVersion.getVersion();
messages.add(new ValidationMessage('Flutter at ${version.flutterRoot}'));
messages.add(new ValidationMessage(
'Framework revision ${version.frameworkRevisionShort} '
'(${version.frameworkAge}), '
'engine revision ${version.engineRevisionShort}'
));
return new ValidationResult(ValidationType.installed, messages,
statusInfo: 'on ${osName()}, channel ${version.channel}');
}
}
class _AtomValidator extends DoctorValidator {
_AtomValidator() : super('Atom - a lightweight development environment for Flutter');
static String _getAtomHomePath() {
final Map<String, String> env = Platform.environment;
if (env['ATOM_HOME'] != null)
return env['ATOM_HOME'];
return os.isWindows
? path.join(env['USERPROFILE'], '.atom')
: path.join(env['HOME'], '.atom');
}
@override
ValidationResult validate() {
List<ValidationMessage> messages = <ValidationMessage>[];
int installCount = 0;
bool atomDirExists = FileSystemEntity.isDirectorySync(_getAtomHomePath());
if (!atomDirExists) {
messages.add(new ValidationMessage.error(
'Atom not installed; download at https://atom.io.'
));
} else {
installCount++;
}
String flutterPluginPath = path.join(_getAtomHomePath(), 'packages', 'flutter');
if (!FileSystemEntity.isDirectorySync(flutterPluginPath)) {
messages.add(new ValidationMessage.error(
'Flutter plugin not installed; this adds Flutter specific functionality to Atom.\n'
'Install the \'flutter\' plugin in Atom or run \'apm install flutter\'.'
));
} else {
installCount++;
try {
File packageFile = new File(path.join(flutterPluginPath, 'package.json'));
dynamic packageInfo = JSON.decode(packageFile.readAsStringSync());
String version = packageInfo['version'];
messages.add(new ValidationMessage('Atom installed; Flutter plugin version $version'));
} catch (error) {
printTrace('Unable to read flutter plugin version: $error');
}
}
return new ValidationResult(
installCount == 2 ? ValidationType.installed : installCount == 1 ? ValidationType.partial : ValidationType.missing,
messages
);
}
}