Commit d5a6fcee authored by Devon Carew's avatar Devon Carew

add a summary option to the doctor class

parent 6a6b6539
......@@ -5,8 +5,8 @@
import 'dart:async';
import 'dart:io';
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
// https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/OVERVIEW.TXT
// https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/SERVICES.TXT
......
......@@ -7,8 +7,8 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
import '../base/globals.dart';
import '../base/os.dart';
import '../globals.dart';
// Android SDK layout:
//
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/globals.dart';
import '../doctor.dart';
import '../globals.dart';
import 'android_sdk.dart';
class AndroidWorkflow extends Workflow {
......@@ -15,8 +15,11 @@ class AndroidWorkflow extends Workflow {
bool get canLaunchDevices => androidSdk != null && androidSdk.validateSdkWellFormed(complain: false);
void diagnose() {
Validator androidValidator = new Validator('Develop for Android devices');
ValidationResult validate() {
Validator androidValidator = new Validator(
'$name toolchain',
description: 'develop for Android devices'
);
Function _sdkExists = () {
return androidSdk == null ? ValidationType.missing : ValidationType.installed;
......@@ -29,6 +32,8 @@ class AndroidWorkflow extends Workflow {
validatorFunction: _sdkExists
));
androidValidator.validate().print();
return androidValidator.validate();
}
void diagnose() => validate().print();
}
......@@ -11,12 +11,12 @@ import 'package:path/path.dart' as path;
import '../android/android_sdk.dart';
import '../application_package.dart';
import '../base/common.dart';
import '../base/globals.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../device.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../toolchain.dart';
import 'adb.dart';
import 'android.dart';
......
......@@ -8,10 +8,10 @@ import 'dart:io';
import 'package:archive/archive.dart';
import 'package:path/path.dart' as path;
import 'base/globals.dart';
import 'base/os.dart';
import 'base/process.dart';
import 'build_configuration.dart';
import 'globals.dart';
String _getNameForHostPlatform(HostPlatform platform) {
switch (platform) {
......
......@@ -6,7 +6,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'globals.dart';
import '../globals.dart';
typedef String StringConverter(String string);
......
......@@ -6,7 +6,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'base/globals.dart';
import 'globals.dart';
enum BuildType {
prebuilt,
......
......@@ -11,14 +11,14 @@ import 'package:den_api/den_api.dart';
import 'package:path/path.dart' as path;
import '../artifacts.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class AnalyzeCommand extends FlutterCommand {
String get name => 'analyze';
String get description => 'Runs a carefully configured dartanalyzer over the current project\'s Dart code.';
String get description => 'Analyze the project\'s Dart code.';
AnalyzeCommand() {
argParser.addFlag('flutter-repo', help: 'Include all the examples and tests from the Flutter repository.', defaultsTo: false);
......
......@@ -11,12 +11,12 @@ import '../android/android_sdk.dart';
import '../application_package.dart';
import '../artifacts.dart';
import '../base/file_system.dart';
import '../base/globals.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../device.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../services.dart';
import '../toolchain.dart';
......
......@@ -4,14 +4,14 @@
import 'dart:async';
import '../base/globals.dart';
import '../flx.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../toolchain.dart';
class BuildCommand extends FlutterCommand {
final String name = 'build';
final String description = 'Packages your Flutter app into an FLX.';
final String description = 'Package your Flutter app into an FLX.';
BuildCommand() {
argParser.addFlag('precompiled', negatable: false);
......
......@@ -7,7 +7,7 @@ import 'dart:async';
import 'package:args/command_runner.dart';
import '../artifacts.dart';
import '../base/globals.dart';
import '../globals.dart';
class CacheCommand extends Command {
final String name = 'cache';
......
......@@ -11,8 +11,8 @@ import 'package:path/path.dart' as path;
import '../android/android.dart' as android;
import '../artifacts.dart';
import '../base/globals.dart';
import '../dart/pub.dart';
import '../globals.dart';
import '../ios/setup_xcodeproj.dart';
class CreateCommand extends Command {
......
......@@ -10,9 +10,9 @@ import '../android/adb.dart';
import '../android/android_sdk.dart';
import '../android/device_android.dart';
import '../base/context.dart';
import '../base/globals.dart';
import '../base/logger.dart';
import '../device.dart';
import '../globals.dart';
import '../ios/device_ios.dart';
import '../ios/simulator.dart';
import '../runner/flutter_command.dart';
......
......@@ -4,8 +4,8 @@
import 'dart:async';
import '../base/globals.dart';
import '../device.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class DevicesCommand extends FlutterCommand {
......
......@@ -5,20 +5,20 @@
import 'dart:async';
import '../artifacts.dart';
import '../base/globals.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../runner/version.dart';
class DoctorCommand extends FlutterCommand {
final String name = 'doctor';
final String description = 'Diagnose the flutter tool.';
final String description = 'Show information about the installed tooling.';
bool get requiresProjectRoot => false;
Future<int> runInProject() async {
// general info
String flutterRoot = ArtifactStore.flutterRoot;
printStatus('Flutter root is $flutterRoot.');
printStatus('Flutter root: $flutterRoot.');
printStatus('');
// doctor
......
......@@ -5,8 +5,8 @@
import 'dart:async';
import 'dart:io';
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
import 'run.dart';
class ListenCommand extends RunCommandBase {
......
......@@ -4,13 +4,13 @@
import 'dart:async';
import '../base/globals.dart';
import '../device.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class LogsCommand extends FlutterCommand {
final String name = 'logs';
final String description = 'Show logs for running Flutter apps.';
final String description = 'Show log output for running Flutter apps.';
LogsCommand() {
argParser.addFlag('clear',
......
......@@ -7,8 +7,8 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../base/globals.dart';
import '../flx.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class RefreshCommand extends FlutterCommand {
......
......@@ -9,11 +9,11 @@ import 'package:path/path.dart' as path;
import '../application_package.dart';
import '../base/common.dart';
import '../base/globals.dart';
import '../build_configuration.dart';
import '../dart/pub.dart';
import '../device.dart';
import '../flx.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../toolchain.dart';
import 'apk.dart';
......
......@@ -8,10 +8,10 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../artifacts.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../runner/flutter_command.dart';
import 'run.dart';
......
......@@ -9,14 +9,14 @@ import 'package:path/path.dart' as path;
import 'package:test/src/executable.dart' as executable;
import '../artifacts.dart';
import '../base/globals.dart';
import '../build_configuration.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../test/loader.dart' as loader;
class TestCommand extends FlutterCommand {
String get name => 'test';
String get description => 'Runs Flutter unit tests for the current project (Linux only).';
String get description => 'Run Flutter unit tests for the current project (Linux only).';
bool get requiresProjectRoot => false;
......
......@@ -6,7 +6,7 @@ import 'dart:async';
import '../android/device_android.dart';
import '../application_package.dart';
import '../base/globals.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class TraceCommand extends FlutterCommand {
......
......@@ -5,8 +5,8 @@
import 'dart:async';
import '../artifacts.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
import '../runner/version.dart';
......
......@@ -7,8 +7,8 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
Future<int> pubGet({
String directory,
......
......@@ -7,8 +7,8 @@ import 'dart:async';
import 'android/device_android.dart';
import 'application_package.dart';
import 'base/common.dart';
import 'base/globals.dart';
import 'build_configuration.dart';
import 'globals.dart';
import 'ios/device_ios.dart';
import 'toolchain.dart';
......
......@@ -4,7 +4,7 @@
import 'android/android_workflow.dart';
import 'base/context.dart';
import 'base/globals.dart';
import 'globals.dart';
import 'ios/ios_workflow.dart';
class Doctor {
......@@ -34,6 +34,35 @@ class Doctor {
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++) {
......@@ -63,6 +92,8 @@ abstract class Workflow {
/// 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();
......@@ -85,20 +116,28 @@ class Validator {
final String resolution;
final ValidationFunction validatorFunction;
List<Validator> _children = [];
List<Validator> _children = <Validator>[];
ValidationResult validate() {
if (validatorFunction != null)
return new ValidationResult(validatorFunction(), this);
List<ValidationResult> childResults;
ValidationType type;
List<ValidationResult> results = _children.map((Validator child) {
return child.validate();
}).toList();
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)
);
}
ValidationType type = _combine(results.map((ValidationResult result) {
return result.type;
}));
return new ValidationResult(type, this, results);
return new ValidationResult(type, this, childResults);
}
ValidationType _combine(Iterable<ValidationType> types) {
......@@ -119,6 +158,15 @@ class ValidationResult {
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);
......@@ -126,15 +174,15 @@ class ValidationResult {
child.print(indent + ' ');
}
void printSelf(String indent) {
void printSelf([String indent = '']) {
String result = indent;
if (type == ValidationType.missing)
result += '[ ] ';
result += '$leadingBox ';
else if (type == ValidationType.installed)
result += '[✓] ';
result += '$leadingBox ';
else
result += '[-] ';
result += '$leadingBox ';
result += '${validator.name} ';
......@@ -151,4 +199,10 @@ class ValidationResult {
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;
}
}
......@@ -14,7 +14,7 @@ import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'base/file_system.dart';
import 'base/globals.dart';
import 'globals.dart';
import 'toolchain.dart';
const String defaultMainPath = 'lib/main.dart';
......
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../android/android_sdk.dart';
import '../device.dart';
import '../doctor.dart';
import '../ios/mac.dart';
import 'context.dart';
import 'logger.dart';
import 'android/android_sdk.dart';
import 'base/context.dart';
import 'base/logger.dart';
import 'device.dart';
import 'doctor.dart';
import 'ios/mac.dart';
DeviceManager get deviceManager => context[DeviceManager];
Logger get logger => context[Logger];
......
......@@ -3,21 +3,21 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import '../application_package.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../device.dart';
import '../globals.dart';
import '../ios/setup_xcodeproj.dart';
import '../services.dart';
import '../toolchain.dart';
import '../ios/setup_xcodeproj.dart';
import 'simulator.dart';
const String _ideviceinstallerInstructions =
......
......@@ -4,9 +4,9 @@
import 'dart:io';
import '../base/globals.dart';
import '../base/process.dart';
import '../doctor.dart';
import '../globals.dart';
class IOSWorkflow extends Workflow {
IOSWorkflow() : super('iOS');
......@@ -20,8 +20,11 @@ class IOSWorkflow extends Workflow {
// for real devices.
bool get canLaunchDevices => xcode.isInstalled;
void diagnose() {
Validator iosValidator = new Validator('Develop for iOS devices');
ValidationResult validate() {
Validator iosValidator = new Validator(
'$name toolchain',
description: 'develop for iOS devices'
);
Function _xcodeExists = () {
return xcode.isInstalled ? ValidationType.installed : ValidationType.missing;
......@@ -48,30 +51,34 @@ class IOSWorkflow extends Workflow {
validatorFunction: _xcodeExists
));
iosValidator.addValidator(new Validator(
Validator brewValidator = new Validator(
'brew',
description: 'install additional development packages',
resolution: 'Download at http://brew.sh/',
validatorFunction: _brewExists
));
);
iosValidator.addValidator(new Validator(
iosValidator.addValidator(brewValidator);
brewValidator.addValidator(new Validator(
'ideviceinstaller',
description: 'discover connected iOS devices',
resolution: "Install via 'brew install ideviceinstaller'",
validatorFunction: _ideviceinstallerExists
));
iosValidator.addValidator(new Validator(
brewValidator.addValidator(new Validator(
'ios-deploy',
description: 'deploy to connected iOS devices',
resolution: "Install via 'brew install ios-deploy'",
validatorFunction: _iosdeployExists
));
iosValidator.validate().print();
return iosValidator.validate();
}
void diagnose() => validate().print();
bool get hasIdeviceId => exitsHappy(<String>['idevice_id', '-h']);
/// Return whether the tooling to list and deploy to real iOS devices (not the
......
......@@ -8,8 +8,8 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../artifacts.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
import '../runner/flutter_command_runner.dart';
/// A map from file path to file contents.
......
......@@ -6,8 +6,8 @@ import 'dart:async';
import 'dart:convert' show JSON;
import 'dart:io';
import '../base/globals.dart';
import '../base/process.dart';
import '../globals.dart';
const String _xcrunPath = '/usr/bin/xcrun';
......
......@@ -9,9 +9,9 @@ import 'package:args/command_runner.dart';
import '../application_package.dart';
import '../artifacts.dart';
import '../base/globals.dart';
import '../build_configuration.dart';
import '../device.dart';
import '../globals.dart';
import '../toolchain.dart';
import 'flutter_command_runner.dart';
......
......@@ -12,9 +12,9 @@ import 'package:path/path.dart' as path;
import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/context.dart';
import '../base/globals.dart';
import '../base/process.dart';
import '../build_configuration.dart';
import '../globals.dart';
import 'version.dart';
const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
......@@ -45,6 +45,7 @@ class FlutterCommandRunner extends CommandRunner {
else
packagesHelp = '\n(required, since the current directory does not contain a "packages" subdirectory)';
argParser.addOption('package-root',
hide: !verboseHelp,
help: 'Path to your packages directory.$packagesHelp');
argParser.addOption('flutter-root',
help: 'The root directory of the Flutter repository. Uses \$$kFlutterRootEnvironmentVariableName if set,\n'
......@@ -122,7 +123,10 @@ class FlutterCommandRunner extends CommandRunner {
defaultsTo: 'out/ios_sim_Release/');
}
final String usageFooter = 'Run "flutter -h -v" for verbose help output, including less commonly used options.';
String get usageFooter =>
'Run "flutter -h -v" for verbose help output, including less commonly used options.\n'
'\n'
'${doctor.summaryText}';
List<BuildConfiguration> get buildConfigurations {
if (_buildConfigurations == null)
......@@ -191,6 +195,8 @@ class FlutterCommandRunner extends CommandRunner {
if (globalResults['version']) {
printStatus(getVersion(ArtifactStore.flutterRoot));
printStatus('');
doctor.summary();
return new Future<int>.value(0);
}
......
......@@ -10,7 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'artifacts.dart';
import 'base/globals.dart';
import 'globals.dart';
const String _kFlutterManifestPath = 'flutter.yaml';
const String _kFlutterServicesManifestPath = 'flutter_services.yaml';
......
......@@ -3,8 +3,8 @@
// found in the LICENSE file.
import 'package:flutter_tools/src/base/context.dart' hide context;
import 'package:flutter_tools/src/base/globals.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/globals.dart';
import 'package:test/test.dart';
main() => defineTests();
......
......@@ -5,9 +5,9 @@
import 'dart:async';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/globals.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/commands/daemon.dart';
import 'package:flutter_tools/src/globals.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
......
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