Skip to content

Commit

Permalink
Add doctor command support (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hidenori Matsubayashi authored Jul 20, 2021
1 parent eacaccf commit 6221fd5
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 55 deletions.
214 changes: 160 additions & 54 deletions lib/elinux_doctor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@

import 'dart:io';

import 'package:flutter_tools/src/android/android_workflow.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/doctor_validator.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/version.dart';
import 'package:meta/meta.dart';

import 'executable.dart';
import 'package:process/process.dart';

ELinuxWorkflow get eLinuxWorkflow => context.get<ELinuxWorkflow>();
ELinuxValidator get eLinuxValidator => context.get<ELinuxValidator>();
Expand All @@ -41,32 +40,170 @@ class ELinuxDoctorValidatorsProvider extends DoctorValidatorsProvider {
];
}

/// A validator that checks for eLinux SDK installation.
class ELinuxValidator extends DoctorValidator {
ELinuxValidator() : super('eLinux toolchain - develop for eLinux devices');

bool _validatePackages(List<ValidationMessage> messages) {
return true;
/// See: [_VersionInfo] in `linux_doctor.dart`
class _VersionInfo {
_VersionInfo(this.description) {
final String versionString = RegExp(r'[0-9]+\.[0-9]+(?:\.[0-9]+)?')
.firstMatch(description)
?.group(0);
number = Version.parse(versionString);
}

/// See: [AndroidValidator.validate] in `android_workflow.dart`
String description;
Version number;
}

/// See: [LinuxDoctorValidator] in `linux_doctor.dart`
class ELinuxValidator extends DoctorValidator {
ELinuxValidator({
@required ProcessManager processManager,
@required UserMessages userMessages,
}) : _processManager = processManager,
_userMessages = userMessages,
super('eLinux toolchain - develop for embedded Linux devices');

final ProcessManager _processManager;
final UserMessages _userMessages;

static const String kClangBinary = 'clang++';
static const String kCmakeBinary = 'cmake';
static const String kPkgConfigBinary = 'pkg-config';

final Map<String, Version> _requiredBinaryVersions = <String, Version>{
kClangBinary: Version(3, 4, 0),
kCmakeBinary: Version(3, 10, 0),
kPkgConfigBinary: Version(0, 29, 0),
};

final List<String> _requiredCommonLibraries = <String>[
'glesv2',
'egl',
];
static const String kRequredCommonLibrariesErrorMessage =
'OpenGL ES/EGL libraries are required for Embedded Linux development. '
'They are likely available from your distribution (e.g.: apt install libegl1-mesa libgles2-mesa)';

@override
Future<ValidationResult> validate() async {
ValidationType validationType = ValidationType.installed;
final List<ValidationMessage> messages = <ValidationMessage>[];

final FlutterVersion version = _FlutterELinuxVersion();
messages.add(ValidationMessage(globals.userMessages.flutterRevision(
version.frameworkRevisionShort,
version.frameworkAge,
version.frameworkCommitDate,
)));
messages.add(ValidationMessage(
globals.userMessages.engineRevision(version.engineRevisionShort)));

if (!_validatePackages(messages)) {
return ValidationResult(ValidationType.partial, messages);
final Map<String, _VersionInfo> installedVersions = <String, _VersionInfo>{
// Sort the check to make the call order predictable for unit tests.
for (String binary in _requiredBinaryVersions.keys.toList()..sort())
binary: await _getBinaryVersion(binary)
};

// Determine overall validation level.
if (installedVersions.values
.any((_VersionInfo versionInfo) => versionInfo?.number == null)) {
validationType = ValidationType.missing;
} else if (installedVersions.keys.any((String binary) =>
installedVersions[binary].number < _requiredBinaryVersions[binary])) {
validationType = ValidationType.partial;
}

// Message for Clang.
{
final _VersionInfo version = installedVersions[kClangBinary];
if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.clangMissing));
} else {
assert(_requiredBinaryVersions.containsKey(kClangBinary));
messages.add(ValidationMessage(version.description));
final Version requiredVersion = _requiredBinaryVersions[kClangBinary];
if (version.number < requiredVersion) {
messages.add(ValidationMessage.error(
_userMessages.clangTooOld(requiredVersion.toString())));
}
}
}

// Message for CMake.
{
final _VersionInfo version = installedVersions[kCmakeBinary];
if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.cmakeMissing));
} else {
assert(_requiredBinaryVersions.containsKey(kCmakeBinary));
messages.add(ValidationMessage(version.description));
final Version requiredVersion = _requiredBinaryVersions[kCmakeBinary];
if (version.number < requiredVersion) {
messages.add(ValidationMessage.error(
_userMessages.cmakeTooOld(requiredVersion.toString())));
}
}
}

// Message for pkg-config.
{
final _VersionInfo version = installedVersions[kPkgConfigBinary];
if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.pkgConfigMissing));
} else {
assert(_requiredBinaryVersions.containsKey(kPkgConfigBinary));
// The full version description is just the number, so add context.
messages.add(ValidationMessage(
_userMessages.pkgConfigVersion(version.description)));
final Version requiredVersion =
_requiredBinaryVersions[kPkgConfigBinary];
if (version.number < requiredVersion) {
messages.add(ValidationMessage.error(
_userMessages.pkgConfigTooOld(requiredVersion.toString())));
}
}
}

// Messages for libraries.
{
bool libraryMissing = false;
for (final String library in _requiredCommonLibraries) {
if (!await _libraryIsPresent(library)) {
libraryMissing = true;
break;
}
}
if (libraryMissing) {
validationType = ValidationType.missing;
messages.add(
const ValidationMessage.error(kRequredCommonLibrariesErrorMessage));
}
}

return ValidationResult(validationType, messages);
}

/// See: [_getBinaryVersion] in `linux_doctor.dart`
Future<_VersionInfo> _getBinaryVersion(String binary) async {
ProcessResult result;
try {
result = await _processManager.run(<String>[
binary,
'--version',
]);
} on ArgumentError {
// ignore error.
}
if (result == null || result.exitCode != 0) {
return null;
}
final String firstLine = (result.stdout as String).split('\n').first.trim();
return _VersionInfo(firstLine);
}

/// See: [_libraryIsPresent] in `linux_doctor.dart`
Future<bool> _libraryIsPresent(String library) async {
ProcessResult result;
try {
result = await _processManager.run(<String>[
'pkg-config',
'--exists',
library,
]);
} on ArgumentError {
// ignore error.
}
return ValidationResult(ValidationType.installed, messages);
return (result?.exitCode ?? 1) == 0;
}
}

Expand All @@ -93,34 +230,3 @@ class ELinuxWorkflow extends Workflow {
@override
bool get canListEmulators => false;
}

class _FlutterELinuxVersion extends FlutterVersion {
_FlutterELinuxVersion() : super(workingDirectory: rootPath);

/// See: [Cache.getVersionFor] in `cache.dart`
String _getVersionFor(String artifactName) {
final File versionFile = globals.fs
.directory(rootPath)
.childDirectory('bin')
.childDirectory('internal')
.childFile('$artifactName.version');
return versionFile.existsSync()
? versionFile.readAsStringSync().trim()
: null;
}

@override
String get engineRevision => _getVersionFor('engine');

/// See: [_runGit] in `version.dart`
String _runGit(String command) => globals.processUtils
.runSync(command.split(' '), workingDirectory: rootPath)
.stdout
.trim();

/// This should be overriden because [FlutterVersion._latestGitCommitDate]
/// runs the git log command in the `Cache.flutterRoot` directory.
@override
String get frameworkCommitDate => _runGit(
'git -c log.showSignature=false log -n 1 --pretty=format:%ad --date=iso');
}
5 changes: 4 additions & 1 deletion lib/executable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ Future<void> main(List<String> args) async {
ELinuxWorkflow: () => ELinuxWorkflow(
operatingSystemUtils: globals.os,
),
ELinuxValidator: () => ELinuxValidator(),
ELinuxValidator: () => ELinuxValidator(
processManager: globals.processManager,
userMessages: globals.userMessages,
),
},
);
}
Expand Down

0 comments on commit 6221fd5

Please sign in to comment.