Merge pull request #1958 from devoncarew/more_validation

More work on validation
This commit is contained in:
Devon Carew 2016-02-17 09:40:46 -08:00
commit ccf08e98de
37 changed files with 146 additions and 74 deletions

View File

@ -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

View File

@ -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:
//

View File

@ -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();
}

View File

@ -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';

View File

@ -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) {

View File

@ -6,7 +6,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'globals.dart';
import '../globals.dart';
typedef String StringConverter(String string);

View File

@ -6,7 +6,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'base/globals.dart';
import 'globals.dart';
enum BuildType {
prebuilt,

View File

@ -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);

View File

@ -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';

View File

@ -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);

View File

@ -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';

View File

@ -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 {

View File

@ -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';

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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',

View File

@ -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 {

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -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 {

View File

@ -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';

View File

@ -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,

View File

@ -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';

View File

@ -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() {
List<ValidationResult> childResults;
ValidationType type;
if (validatorFunction != null)
return new ValidationResult(validatorFunction(), this);
type = validatorFunction();
List<ValidationResult> results = _children.map((Validator child) {
return child.validate();
}).toList();
childResults = _children.map((Validator child) => child.validate()).toList();
ValidationType type = _combine(results.map((ValidationResult result) {
return result.type;
}));
return new ValidationResult(type, this, results);
// 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) {
@ -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;
}
}

View File

@ -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';

View File

@ -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];

View File

@ -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 =

View File

@ -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

View File

@ -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.

View File

@ -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';

View File

@ -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';

View File

@ -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);
}

View File

@ -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';

View File

@ -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();

View File

@ -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';