blob: d5f1d99ede1cf25120bfb43e692580931837e928 [file]
// 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 'android/android_workflow.dart';
import 'base/context.dart';
import 'globals.dart';
import 'ios/ios_workflow.dart';
class Doctor {
Doctor() {
_iosWorkflow = new IOSWorkflow();
if (_iosWorkflow.appliesToHostPlatform)
_workflows.add(_iosWorkflow);
_androidWorkflow = new AndroidWorkflow();
if (_androidWorkflow.appliesToHostPlatform)
_workflows.add(_androidWorkflow);
}
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<Workflow> _workflows = <Workflow>[];
List<Workflow> get workflows => _workflows;
/// 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 (Workflow workflow in workflows) {
ValidationResult result = workflow.validate();
buffer.write('${result.leadingBox} The ${workflow.name} toolchain is ');
if (result.type == ValidationType.missing)
buffer.writeln('not installed.');
else if (result.type == ValidationType.partial)
buffer.writeln('partially installed; more components are available.');
else
buffer.writeln('fully installed.');
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() {
for (int i = 0; i < workflows.length; i++) {
if (i > 0)
printStatus('');
workflows[i].diagnose();
}
}
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 {
Workflow(this.name);
final String name;
/// 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;
ValidationResult validate();
/// Print verbose information about the state of the workflow.
void diagnose();
String toString() => name;
}
enum ValidationType {
missing,
partial,
installed
}
typedef ValidationType ValidationFunction();
class Validator {
Validator(this.name, { this.description, this.resolution, this.validatorFunction });
final String name;
final String description;
final String resolution;
final ValidationFunction validatorFunction;
List<Validator> _children = <Validator>[];
ValidationResult validate() {
List<ValidationResult> childResults;
ValidationType type;
if (validatorFunction != null)
type = validatorFunction();
childResults = _children.map((Validator child) => child.validate()).toList();
// If there's no immediate validator, the result we return is synthesized
// from the sub-tree of children. This is so we can show that the branch is
// not fully installed.
if (type == null) {
type = _combine(childResults
.expand((ValidationResult child) => child._allResults)
.map((ValidationResult result) => result.type)
);
}
return new ValidationResult(type, this, childResults);
}
ValidationType _combine(Iterable<ValidationType> types) {
if (types.contains(ValidationType.missing) && types.contains(ValidationType.installed))
return ValidationType.partial;
if (types.contains(ValidationType.missing))
return ValidationType.missing;
return ValidationType.installed;
}
void addValidator(Validator validator) => _children.add(validator);
}
class ValidationResult {
ValidationResult(this.type, this.validator, [this.childResults = const <ValidationResult>[]]);
final ValidationType type;
final Validator validator;
final List<ValidationResult> childResults;
String get leadingBox {
if (type == ValidationType.missing)
return '[ ]';
else if (type == ValidationType.installed)
return '[✓]';
else
return '[-]';
}
void print([String indent = '']) {
printSelf(indent);
for (ValidationResult child in childResults)
child.print(indent + ' ');
}
void printSelf([String indent = '']) {
String result = indent;
if (type == ValidationType.missing)
result += '$leadingBox ';
else if (type == ValidationType.installed)
result += '$leadingBox ';
else
result += '$leadingBox ';
result += '${validator.name} ';
if (validator.description != null)
result += '- ${validator.description} ';
if (type == ValidationType.missing)
result += '(missing)';
else if (type == ValidationType.installed)
result += '(installed)';
printStatus(result);
if (type == ValidationType.missing && validator.resolution != null)
printStatus('$indent ${validator.resolution}');
}
List<ValidationResult> get _allResults {
List<ValidationResult> results = <ValidationResult>[this];
results.addAll(childResults);
return results;
}
}