From 6221fd59b71e4f3b1358ce10a9bd7fa31d70c8f3 Mon Sep 17 00:00:00 2001 From: Hidenori Matsubayashi Date: Tue, 20 Jul 2021 14:50:50 +0900 Subject: [PATCH] Add doctor command support (#11) --- lib/elinux_doctor.dart | 212 ++++++++++++++++++++++++++++++----------- lib/executable.dart | 5 +- 2 files changed, 163 insertions(+), 54 deletions(-) diff --git a/lib/elinux_doctor.dart b/lib/elinux_doctor.dart index fb5cc1f..afaccef 100644 --- a/lib/elinux_doctor.dart +++ b/lib/elinux_doctor.dart @@ -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(); ELinuxValidator get eLinuxValidator => context.get(); @@ -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 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 _requiredBinaryVersions = { + kClangBinary: Version(3, 4, 0), + kCmakeBinary: Version(3, 10, 0), + kPkgConfigBinary: Version(0, 29, 0), + }; + + final List _requiredCommonLibraries = [ + '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 validate() async { + ValidationType validationType = ValidationType.installed; final List messages = []; - final FlutterVersion version = _FlutterELinuxVersion(); - messages.add(ValidationMessage(globals.userMessages.flutterRevision( - version.frameworkRevisionShort, - version.frameworkAge, - version.frameworkCommitDate, - ))); - messages.add(ValidationMessage( - globals.userMessages.engineRevision(version.engineRevisionShort))); + final Map installedVersions = { + // Sort the check to make the call order predictable for unit tests. + for (String binary in _requiredBinaryVersions.keys.toList()..sort()) + binary: await _getBinaryVersion(binary) + }; - if (!_validatePackages(messages)) { - return ValidationResult(ValidationType.partial, messages); + // 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; } - return ValidationResult(ValidationType.installed, messages); + + // 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([ + 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 _libraryIsPresent(String library) async { + ProcessResult result; + try { + result = await _processManager.run([ + 'pkg-config', + '--exists', + library, + ]); + } on ArgumentError { + // ignore error. + } + return (result?.exitCode ?? 1) == 0; } } @@ -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'); -} diff --git a/lib/executable.dart b/lib/executable.dart index 258442f..0b9014c 100644 --- a/lib/executable.dart +++ b/lib/executable.dart @@ -129,7 +129,10 @@ Future main(List args) async { ELinuxWorkflow: () => ELinuxWorkflow( operatingSystemUtils: globals.os, ), - ELinuxValidator: () => ELinuxValidator(), + ELinuxValidator: () => ELinuxValidator( + processManager: globals.processManager, + userMessages: globals.userMessages, + ), }, ); }