diff --git a/packages/flutter_tools/lib/src/linux/linux_doctor.dart b/packages/flutter_tools/lib/src/linux/linux_doctor.dart index 1b5bfeba652..a055f3dbe2e 100644 --- a/packages/flutter_tools/lib/src/linux/linux_doctor.dart +++ b/packages/flutter_tools/lib/src/linux/linux_doctor.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - -import 'package:meta/meta.dart'; import 'package:process/process.dart'; import '../base/io.dart'; @@ -19,7 +16,7 @@ class _VersionInfo { /// This should contain a version number. For example: /// "clang version 9.0.1-6+build1" _VersionInfo(this.description) { - final String versionString = RegExp(r'[0-9]+\.[0-9]+(?:\.[0-9]+)?').firstMatch(description).group(0); + final String? versionString = RegExp(r'[0-9]+\.[0-9]+(?:\.[0-9]+)?').firstMatch(description)?.group(0); number = Version.parse(versionString); } @@ -27,14 +24,14 @@ class _VersionInfo { String description; // The parsed Version. - Version number; + Version? number; } /// A validator that checks for Clang and Make build dependencies. class LinuxDoctorValidator extends DoctorValidator { LinuxDoctorValidator({ - @required ProcessManager processManager, - @required UserMessages userMessages, + required ProcessManager processManager, + required UserMessages userMessages, }) : _processManager = processManager, _userMessages = userMessages, super('Linux toolchain - develop for Linux desktop'); @@ -65,29 +62,30 @@ class LinuxDoctorValidator extends DoctorValidator { ValidationType validationType = ValidationType.installed; final List messages = []; - final Map installedVersions = { + 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) }; // Determine overall validation level. - if (installedVersions.values.contains(null)) { + if (installedVersions.values.any((_VersionInfo? versionInfo) => versionInfo?.number == null)) { validationType = ValidationType.missing; } else if (installedVersions.keys.any((String binary) => - installedVersions[binary].number < _requiredBinaryVersions[binary])) { + installedVersions[binary]!.number! < _requiredBinaryVersions[binary]!)) { validationType = ValidationType.partial; } // Message for Clang. { - final _VersionInfo version = installedVersions[kClangBinary]; - if (version == null) { + 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) { + final Version requiredVersion = _requiredBinaryVersions[kClangBinary]!; + if (version.number! < requiredVersion) { messages.add(ValidationMessage.error(_userMessages.clangTooOld(requiredVersion.toString()))); } } @@ -95,13 +93,14 @@ class LinuxDoctorValidator extends DoctorValidator { // Message for CMake. { - final _VersionInfo version = installedVersions[kCmakeBinary]; - if (version == null) { + 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) { + final Version requiredVersion = _requiredBinaryVersions[kCmakeBinary]!; + if (version.number! < requiredVersion) { messages.add(ValidationMessage.error(_userMessages.cmakeTooOld(requiredVersion.toString()))); } } @@ -109,14 +108,15 @@ class LinuxDoctorValidator extends DoctorValidator { // Message for ninja. { - final _VersionInfo version = installedVersions[kNinjaBinary]; - if (version == null) { + final _VersionInfo? version = installedVersions[kNinjaBinary]; + if (version == null || version.number == null) { messages.add(ValidationMessage.error(_userMessages.ninjaMissing)); } else { + assert(_requiredBinaryVersions.containsKey(kNinjaBinary)); // The full version description is just the number, so add context. messages.add(ValidationMessage(_userMessages.ninjaVersion(version.description))); - final Version requiredVersion = _requiredBinaryVersions[kNinjaBinary]; - if (version.number < requiredVersion) { + final Version requiredVersion = _requiredBinaryVersions[kNinjaBinary]!; + if (version.number! < requiredVersion) { messages.add(ValidationMessage.error(_userMessages.ninjaTooOld(requiredVersion.toString()))); } } @@ -124,14 +124,15 @@ class LinuxDoctorValidator extends DoctorValidator { // Message for pkg-config. { - final _VersionInfo version = installedVersions[kPkgConfigBinary]; - if (version == null) { + 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) { + final Version requiredVersion = _requiredBinaryVersions[kPkgConfigBinary]!; + if (version.number! < requiredVersion) { messages.add(ValidationMessage.error(_userMessages.pkgConfigTooOld(requiredVersion.toString()))); } } @@ -159,8 +160,8 @@ class LinuxDoctorValidator extends DoctorValidator { /// /// Requires tha [binary] take a '--version' flag, and print a version of the /// form x.y.z somewhere on the first line of output. - Future<_VersionInfo> _getBinaryVersion(String binary) async { - ProcessResult result; + Future<_VersionInfo?> _getBinaryVersion(String binary) async { + ProcessResult? result; try { result = await _processManager.run([ binary, @@ -178,7 +179,7 @@ class LinuxDoctorValidator extends DoctorValidator { /// Checks that [library] is available via pkg-config. Future _libraryIsPresent(String library) async { - ProcessResult result; + ProcessResult? result; try { result = await _processManager.run([ 'pkg-config', diff --git a/packages/flutter_tools/test/general.shard/linux/linux_doctor_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_doctor_test.dart index db13677bb7c..cb2aa6423f0 100644 --- a/packages/flutter_tools/test/general.shard/linux/linux_doctor_test.dart +++ b/packages/flutter_tools/test/general.shard/linux/linux_doctor_test.dart @@ -236,6 +236,30 @@ void main() { ]); }); + testWithoutContext('Missing validation when CMake version is unparsable', () async { + final ProcessManager processManager = FakeProcessManager.list([ + _clangPresentCommand('4.0.1'), + _cmakePresentCommand('bogus'), + _ninjaPresentCommand('1.10.0'), + _pkgConfigPresentCommand('0.29'), + ..._gtkLibrariesPresentCommands(), + ]); + final UserMessages userMessages = UserMessages(); + final DoctorValidator linuxDoctorValidator = LinuxDoctorValidator( + processManager: processManager, + userMessages: userMessages, + ); + final ValidationResult result = await linuxDoctorValidator.validate(); + + expect(result.type, ValidationType.missing); + expect(result.messages, [ + const ValidationMessage('clang version 4.0.1-6+build1'), + ValidationMessage.error(userMessages.cmakeMissing), + const ValidationMessage('ninja version 1.10.0'), + const ValidationMessage('pkg-config version 0.29'), + ]); + }); + testWithoutContext('Missing validation when clang++ is not available', () async { final ProcessManager processManager = FakeProcessManager.list([ _missingBinaryCommand('clang++'), @@ -260,6 +284,30 @@ void main() { ]); }); + testWithoutContext('Missing validation when clang++ version is unparsable', () async { + final ProcessManager processManager = FakeProcessManager.list([ + _clangPresentCommand('bogus'), + _cmakePresentCommand('3.16.3'), + _ninjaPresentCommand('1.10.0'), + _pkgConfigPresentCommand('0.29'), + ..._gtkLibrariesPresentCommands(), + ]); + final UserMessages userMessages = UserMessages(); + final DoctorValidator linuxDoctorValidator = LinuxDoctorValidator( + processManager: processManager, + userMessages: userMessages, + ); + final ValidationResult result = await linuxDoctorValidator.validate(); + + expect(result.type, ValidationType.missing); + expect(result.messages, [ + ValidationMessage.error(userMessages.clangMissing), + const ValidationMessage('cmake version 3.16.3'), + const ValidationMessage('ninja version 1.10.0'), + const ValidationMessage('pkg-config version 0.29'), + ]); + }); + testWithoutContext('Missing validation when ninja is not available', () async { final ProcessManager processManager = FakeProcessManager.list([ _clangPresentCommand('4.0.1'), @@ -284,6 +332,30 @@ void main() { ]); }); + testWithoutContext('Missing validation when ninja version is unparsable', () async { + final ProcessManager processManager = FakeProcessManager.list([ + _clangPresentCommand('4.0.1'), + _cmakePresentCommand('3.16.3'), + _ninjaPresentCommand('bogus'), + _pkgConfigPresentCommand('0.29'), + ..._gtkLibrariesPresentCommands(), + ]); + final UserMessages userMessages = UserMessages(); + final DoctorValidator linuxDoctorValidator = LinuxDoctorValidator( + processManager: processManager, + userMessages: userMessages, + ); + final ValidationResult result = await linuxDoctorValidator.validate(); + + expect(result.type, ValidationType.missing); + expect(result.messages, [ + const ValidationMessage('clang version 4.0.1-6+build1'), + const ValidationMessage('cmake version 3.16.3'), + ValidationMessage.error(userMessages.ninjaMissing), + const ValidationMessage('pkg-config version 0.29'), + ]); + }); + testWithoutContext('Missing validation when pkg-config is not available', () async { final ProcessManager processManager = FakeProcessManager.list([ _clangPresentCommand('4.0.1'), @@ -308,6 +380,30 @@ void main() { ]); }); + testWithoutContext('Missing validation when pkg-config version is unparsable', () async { + final ProcessManager processManager = FakeProcessManager.list([ + _clangPresentCommand('4.0.1'), + _cmakePresentCommand('3.16.3'), + _ninjaPresentCommand('1.10.0'), + _pkgConfigPresentCommand('bogus'), + ..._gtkLibrariesPresentCommands(), + ]); + final UserMessages userMessages = UserMessages(); + final DoctorValidator linuxDoctorValidator = LinuxDoctorValidator( + processManager: processManager, + userMessages: userMessages, + ); + final ValidationResult result = await linuxDoctorValidator.validate(); + + expect(result.type, ValidationType.missing); + expect(result.messages, [ + const ValidationMessage('clang version 4.0.1-6+build1'), + const ValidationMessage('cmake version 3.16.3'), + const ValidationMessage('ninja version 1.10.0'), + ValidationMessage.error(userMessages.pkgConfigMissing), + ]); + }); + testWithoutContext('Missing validation when GTK libraries are not available', () async { final ProcessManager processManager = FakeProcessManager.list([ _clangPresentCommand('4.0.1'),