Unverified Commit b481d4b2 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate linux_doctor to null safety (#79804)

parent 32440ce1
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import '../base/io.dart'; import '../base/io.dart';
...@@ -19,7 +16,7 @@ class _VersionInfo { ...@@ -19,7 +16,7 @@ class _VersionInfo {
/// This should contain a version number. For example: /// This should contain a version number. For example:
/// "clang version 9.0.1-6+build1" /// "clang version 9.0.1-6+build1"
_VersionInfo(this.description) { _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); number = Version.parse(versionString);
} }
...@@ -27,14 +24,14 @@ class _VersionInfo { ...@@ -27,14 +24,14 @@ class _VersionInfo {
String description; String description;
// The parsed Version. // The parsed Version.
Version number; Version? number;
} }
/// A validator that checks for Clang and Make build dependencies. /// A validator that checks for Clang and Make build dependencies.
class LinuxDoctorValidator extends DoctorValidator { class LinuxDoctorValidator extends DoctorValidator {
LinuxDoctorValidator({ LinuxDoctorValidator({
@required ProcessManager processManager, required ProcessManager processManager,
@required UserMessages userMessages, required UserMessages userMessages,
}) : _processManager = processManager, }) : _processManager = processManager,
_userMessages = userMessages, _userMessages = userMessages,
super('Linux toolchain - develop for Linux desktop'); super('Linux toolchain - develop for Linux desktop');
...@@ -65,29 +62,30 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -65,29 +62,30 @@ class LinuxDoctorValidator extends DoctorValidator {
ValidationType validationType = ValidationType.installed; ValidationType validationType = ValidationType.installed;
final List<ValidationMessage> messages = <ValidationMessage>[]; final List<ValidationMessage> messages = <ValidationMessage>[];
final Map<String, _VersionInfo> installedVersions = <String, _VersionInfo>{ final Map<String, _VersionInfo?> installedVersions = <String, _VersionInfo?>{
// Sort the check to make the call order predictable for unit tests. // Sort the check to make the call order predictable for unit tests.
for (String binary in _requiredBinaryVersions.keys.toList()..sort()) for (String binary in _requiredBinaryVersions.keys.toList()..sort())
binary: await _getBinaryVersion(binary) binary: await _getBinaryVersion(binary)
}; };
// Determine overall validation level. // Determine overall validation level.
if (installedVersions.values.contains(null)) { if (installedVersions.values.any((_VersionInfo? versionInfo) => versionInfo?.number == null)) {
validationType = ValidationType.missing; validationType = ValidationType.missing;
} else if (installedVersions.keys.any((String binary) => } else if (installedVersions.keys.any((String binary) =>
installedVersions[binary].number < _requiredBinaryVersions[binary])) { installedVersions[binary]!.number! < _requiredBinaryVersions[binary]!)) {
validationType = ValidationType.partial; validationType = ValidationType.partial;
} }
// Message for Clang. // Message for Clang.
{ {
final _VersionInfo version = installedVersions[kClangBinary]; final _VersionInfo? version = installedVersions[kClangBinary];
if (version == null) { if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.clangMissing)); messages.add(ValidationMessage.error(_userMessages.clangMissing));
} else { } else {
assert(_requiredBinaryVersions.containsKey(kClangBinary));
messages.add(ValidationMessage(version.description)); messages.add(ValidationMessage(version.description));
final Version requiredVersion = _requiredBinaryVersions[kClangBinary]; final Version requiredVersion = _requiredBinaryVersions[kClangBinary]!;
if (version.number < requiredVersion) { if (version.number! < requiredVersion) {
messages.add(ValidationMessage.error(_userMessages.clangTooOld(requiredVersion.toString()))); messages.add(ValidationMessage.error(_userMessages.clangTooOld(requiredVersion.toString())));
} }
} }
...@@ -95,13 +93,14 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -95,13 +93,14 @@ class LinuxDoctorValidator extends DoctorValidator {
// Message for CMake. // Message for CMake.
{ {
final _VersionInfo version = installedVersions[kCmakeBinary]; final _VersionInfo? version = installedVersions[kCmakeBinary];
if (version == null) { if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.cmakeMissing)); messages.add(ValidationMessage.error(_userMessages.cmakeMissing));
} else { } else {
assert(_requiredBinaryVersions.containsKey(kCmakeBinary));
messages.add(ValidationMessage(version.description)); messages.add(ValidationMessage(version.description));
final Version requiredVersion = _requiredBinaryVersions[kCmakeBinary]; final Version requiredVersion = _requiredBinaryVersions[kCmakeBinary]!;
if (version.number < requiredVersion) { if (version.number! < requiredVersion) {
messages.add(ValidationMessage.error(_userMessages.cmakeTooOld(requiredVersion.toString()))); messages.add(ValidationMessage.error(_userMessages.cmakeTooOld(requiredVersion.toString())));
} }
} }
...@@ -109,14 +108,15 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -109,14 +108,15 @@ class LinuxDoctorValidator extends DoctorValidator {
// Message for ninja. // Message for ninja.
{ {
final _VersionInfo version = installedVersions[kNinjaBinary]; final _VersionInfo? version = installedVersions[kNinjaBinary];
if (version == null) { if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.ninjaMissing)); messages.add(ValidationMessage.error(_userMessages.ninjaMissing));
} else { } else {
assert(_requiredBinaryVersions.containsKey(kNinjaBinary));
// The full version description is just the number, so add context. // The full version description is just the number, so add context.
messages.add(ValidationMessage(_userMessages.ninjaVersion(version.description))); messages.add(ValidationMessage(_userMessages.ninjaVersion(version.description)));
final Version requiredVersion = _requiredBinaryVersions[kNinjaBinary]; final Version requiredVersion = _requiredBinaryVersions[kNinjaBinary]!;
if (version.number < requiredVersion) { if (version.number! < requiredVersion) {
messages.add(ValidationMessage.error(_userMessages.ninjaTooOld(requiredVersion.toString()))); messages.add(ValidationMessage.error(_userMessages.ninjaTooOld(requiredVersion.toString())));
} }
} }
...@@ -124,14 +124,15 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -124,14 +124,15 @@ class LinuxDoctorValidator extends DoctorValidator {
// Message for pkg-config. // Message for pkg-config.
{ {
final _VersionInfo version = installedVersions[kPkgConfigBinary]; final _VersionInfo? version = installedVersions[kPkgConfigBinary];
if (version == null) { if (version == null || version.number == null) {
messages.add(ValidationMessage.error(_userMessages.pkgConfigMissing)); messages.add(ValidationMessage.error(_userMessages.pkgConfigMissing));
} else { } else {
assert(_requiredBinaryVersions.containsKey(kPkgConfigBinary));
// The full version description is just the number, so add context. // The full version description is just the number, so add context.
messages.add(ValidationMessage(_userMessages.pkgConfigVersion(version.description))); messages.add(ValidationMessage(_userMessages.pkgConfigVersion(version.description)));
final Version requiredVersion = _requiredBinaryVersions[kPkgConfigBinary]; final Version requiredVersion = _requiredBinaryVersions[kPkgConfigBinary]!;
if (version.number < requiredVersion) { if (version.number! < requiredVersion) {
messages.add(ValidationMessage.error(_userMessages.pkgConfigTooOld(requiredVersion.toString()))); messages.add(ValidationMessage.error(_userMessages.pkgConfigTooOld(requiredVersion.toString())));
} }
} }
...@@ -159,8 +160,8 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -159,8 +160,8 @@ class LinuxDoctorValidator extends DoctorValidator {
/// ///
/// Requires tha [binary] take a '--version' flag, and print a version of the /// Requires tha [binary] take a '--version' flag, and print a version of the
/// form x.y.z somewhere on the first line of output. /// form x.y.z somewhere on the first line of output.
Future<_VersionInfo> _getBinaryVersion(String binary) async { Future<_VersionInfo?> _getBinaryVersion(String binary) async {
ProcessResult result; ProcessResult? result;
try { try {
result = await _processManager.run(<String>[ result = await _processManager.run(<String>[
binary, binary,
...@@ -178,7 +179,7 @@ class LinuxDoctorValidator extends DoctorValidator { ...@@ -178,7 +179,7 @@ class LinuxDoctorValidator extends DoctorValidator {
/// Checks that [library] is available via pkg-config. /// Checks that [library] is available via pkg-config.
Future<bool> _libraryIsPresent(String library) async { Future<bool> _libraryIsPresent(String library) async {
ProcessResult result; ProcessResult? result;
try { try {
result = await _processManager.run(<String>[ result = await _processManager.run(<String>[
'pkg-config', 'pkg-config',
......
...@@ -236,6 +236,30 @@ void main() { ...@@ -236,6 +236,30 @@ void main() {
]); ]);
}); });
testWithoutContext('Missing validation when CMake version is unparsable', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_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, <ValidationMessage>[
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 { testWithoutContext('Missing validation when clang++ is not available', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_missingBinaryCommand('clang++'), _missingBinaryCommand('clang++'),
...@@ -260,6 +284,30 @@ void main() { ...@@ -260,6 +284,30 @@ void main() {
]); ]);
}); });
testWithoutContext('Missing validation when clang++ version is unparsable', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_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>[
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 { testWithoutContext('Missing validation when ninja is not available', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_clangPresentCommand('4.0.1'), _clangPresentCommand('4.0.1'),
...@@ -284,6 +332,30 @@ void main() { ...@@ -284,6 +332,30 @@ void main() {
]); ]);
}); });
testWithoutContext('Missing validation when ninja version is unparsable', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_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, <ValidationMessage>[
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 { testWithoutContext('Missing validation when pkg-config is not available', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_clangPresentCommand('4.0.1'), _clangPresentCommand('4.0.1'),
...@@ -308,6 +380,30 @@ void main() { ...@@ -308,6 +380,30 @@ void main() {
]); ]);
}); });
testWithoutContext('Missing validation when pkg-config version is unparsable', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_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, <ValidationMessage>[
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 { testWithoutContext('Missing validation when GTK libraries are not available', () async {
final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ final ProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
_clangPresentCommand('4.0.1'), _clangPresentCommand('4.0.1'),
......
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