mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Migrate visual_studio to null safety (#78942)
This commit is contained in:
parent
c49cd2719d
commit
f712fecde2
@ -2,11 +2,9 @@
|
||||
// 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/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
@ -18,10 +16,10 @@ import '../convert.dart';
|
||||
/// Encapsulates information about the installed copy of Visual Studio, if any.
|
||||
class VisualStudio {
|
||||
VisualStudio({
|
||||
@required FileSystem fileSystem,
|
||||
@required ProcessManager processManager,
|
||||
@required Platform platform,
|
||||
@required Logger logger,
|
||||
required FileSystem fileSystem,
|
||||
required ProcessManager processManager,
|
||||
required Platform platform,
|
||||
required Logger logger,
|
||||
}) : _platform = platform,
|
||||
_fileSystem = fileSystem,
|
||||
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
|
||||
@ -38,7 +36,7 @@ class VisualStudio {
|
||||
bool get isInstalled => _bestVisualStudioDetails.isNotEmpty;
|
||||
|
||||
bool get isAtLeastMinimumVersion {
|
||||
final int installedMajorVersion = _majorVersion;
|
||||
final int? installedMajorVersion = _majorVersion;
|
||||
return installedMajorVersion != null && installedMajorVersion >= _minimumSupportedVersion;
|
||||
}
|
||||
|
||||
@ -54,7 +52,7 @@ class VisualStudio {
|
||||
/// The user-friendly version number of the Visual Studio install.
|
||||
///
|
||||
/// For instance: "15.4.0".
|
||||
String get displayVersion {
|
||||
String? get displayVersion {
|
||||
if (_bestVisualStudioDetails[_catalogKey] == null) {
|
||||
return null;
|
||||
}
|
||||
@ -80,7 +78,7 @@ class VisualStudio {
|
||||
if (_bestVisualStudioDetails.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return _bestVisualStudioDetails[_isCompleteKey] as bool ?? true;
|
||||
return _bestVisualStudioDetails[_isCompleteKey] as bool? ?? true;
|
||||
}
|
||||
|
||||
/// True if Visual Studio is launchable.
|
||||
@ -90,14 +88,14 @@ class VisualStudio {
|
||||
if (_bestVisualStudioDetails.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return _bestVisualStudioDetails[_isLaunchableKey] as bool ?? true;
|
||||
return _bestVisualStudioDetails[_isLaunchableKey] as bool? ?? true;
|
||||
}
|
||||
|
||||
/// True if the Visual Studio installation is as pre-release version.
|
||||
bool get isPrerelease => _bestVisualStudioDetails[_isPrereleaseKey] as bool ?? false;
|
||||
bool get isPrerelease => _bestVisualStudioDetails[_isPrereleaseKey] as bool? ?? false;
|
||||
|
||||
/// True if a reboot is required to complete the Visual Studio installation.
|
||||
bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey] as bool ?? false;
|
||||
bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey] as bool? ?? false;
|
||||
|
||||
/// The name of the recommended Visual Studio installer workload.
|
||||
String get workloadDescription => 'Desktop development with C++';
|
||||
@ -106,8 +104,8 @@ class VisualStudio {
|
||||
/// found.
|
||||
///
|
||||
/// For instance: 10.0.18362.0.
|
||||
String getWindows10SDKVersion() {
|
||||
final String sdkLocation = _getWindows10SdkLocation();
|
||||
String? getWindows10SDKVersion() {
|
||||
final String? sdkLocation = _getWindows10SdkLocation();
|
||||
if (sdkLocation == null) {
|
||||
return null;
|
||||
}
|
||||
@ -116,13 +114,13 @@ class VisualStudio {
|
||||
return null;
|
||||
}
|
||||
// The directories in this folder are named by the SDK version.
|
||||
Version highestVersion;
|
||||
Version? highestVersion;
|
||||
for (final FileSystemEntity versionEntry in sdkIncludeDirectory.listSync()) {
|
||||
if (versionEntry.basename.startsWith('10.')) {
|
||||
// Version only handles 3 components; strip off the '10.' to leave three
|
||||
// components, since they all start with that.
|
||||
final Version version = Version.parse(versionEntry.basename.substring(3));
|
||||
if (highestVersion == null || version > highestVersion) {
|
||||
final Version? version = Version.parse(versionEntry.basename.substring(3));
|
||||
if (highestVersion == null || (version != null && version > highestVersion)) {
|
||||
highestVersion = version;
|
||||
}
|
||||
}
|
||||
@ -151,7 +149,7 @@ class VisualStudio {
|
||||
|
||||
/// The path to CMake, or null if no Visual Studio installation has
|
||||
/// the components necessary to build.
|
||||
String get cmakePath {
|
||||
String? get cmakePath {
|
||||
final Map<String, dynamic> details = _usableVisualStudioDetails;
|
||||
if (details.isEmpty) {
|
||||
return null;
|
||||
@ -170,7 +168,7 @@ class VisualStudio {
|
||||
}
|
||||
|
||||
/// The major version of the Visual Studio install, as an integer.
|
||||
int get _majorVersion => fullVersion != null ? int.tryParse(fullVersion.split('.')[0]) : null;
|
||||
int? get _majorVersion => fullVersion != null ? int.tryParse(fullVersion.split('.')[0]) : null;
|
||||
|
||||
/// The path to vswhere.exe.
|
||||
///
|
||||
@ -178,12 +176,18 @@ class VisualStudio {
|
||||
/// present then there isn't a new enough installation of VS. This path is
|
||||
/// not user-controllable, unlike the install location of Visual Studio
|
||||
/// itself.
|
||||
String get _vswherePath => _fileSystem.path.join(
|
||||
_platform.environment['PROGRAMFILES(X86)'],
|
||||
'Microsoft Visual Studio',
|
||||
'Installer',
|
||||
'vswhere.exe',
|
||||
);
|
||||
String get _vswherePath {
|
||||
const String programFilesEnv = 'PROGRAMFILES(X86)';
|
||||
if (!_platform.environment.containsKey(programFilesEnv)) {
|
||||
throwToolExit('%$programFilesEnv% environment variable not found.');
|
||||
}
|
||||
return _fileSystem.path.join(
|
||||
_platform.environment[programFilesEnv]!,
|
||||
'Microsoft Visual Studio',
|
||||
'Installer',
|
||||
'vswhere.exe',
|
||||
);
|
||||
}
|
||||
|
||||
/// Workload ID for use with vswhere requirements.
|
||||
///
|
||||
@ -198,7 +202,7 @@ class VisualStudio {
|
||||
///
|
||||
/// Maps from component IDs to description in the installer UI.
|
||||
/// See https://docs.microsoft.com/en-us/visualstudio/install/workload-and-component-ids
|
||||
Map<String, String> _requiredComponents([int majorVersion]) {
|
||||
Map<String, String> _requiredComponents([int? majorVersion]) {
|
||||
// The description of the C++ toolchain required by the template. The
|
||||
// component name is significantly different in different versions.
|
||||
// When a new major version of VS is supported, its toolchain description
|
||||
@ -273,15 +277,17 @@ class VisualStudio {
|
||||
/// Returns the details dictionary for the newest version of Visual Studio.
|
||||
/// If [validateRequirements] is set, the search will be limited to versions
|
||||
/// that have all of the required workloads and components.
|
||||
Map<String, dynamic> _visualStudioDetails({
|
||||
Map<String, dynamic>? _visualStudioDetails({
|
||||
bool validateRequirements = false,
|
||||
List<String> additionalArguments,
|
||||
String requiredWorkload
|
||||
List<String>? additionalArguments,
|
||||
String? requiredWorkload
|
||||
}) {
|
||||
final List<String> requirementArguments = validateRequirements
|
||||
? <String>[
|
||||
'-requires',
|
||||
requiredWorkload,
|
||||
if (requiredWorkload != null) ...<String>[
|
||||
'-requires',
|
||||
requiredWorkload,
|
||||
],
|
||||
..._requiredComponents(_minimumSupportedVersion).keys
|
||||
]
|
||||
: <String>[];
|
||||
@ -296,7 +302,7 @@ class VisualStudio {
|
||||
_vswherePath,
|
||||
...defaultArguments,
|
||||
...?additionalArguments,
|
||||
...?requirementArguments,
|
||||
...requirementArguments,
|
||||
], encoding: utf8);
|
||||
if (whereResult.exitCode == 0) {
|
||||
final List<Map<String, dynamic>> installations =
|
||||
@ -342,16 +348,16 @@ class VisualStudio {
|
||||
///
|
||||
/// If no installation is found, the cached VS details are set to an empty map
|
||||
/// to avoid repeating vswhere queries that have already not found an installation.
|
||||
Map<String, dynamic> _cachedUsableVisualStudioDetails;
|
||||
Map<String, dynamic>? _cachedUsableVisualStudioDetails;
|
||||
Map<String, dynamic> get _usableVisualStudioDetails {
|
||||
if (_cachedUsableVisualStudioDetails != null) {
|
||||
return _cachedUsableVisualStudioDetails;
|
||||
return _cachedUsableVisualStudioDetails!;
|
||||
}
|
||||
final List<String> minimumVersionArguments = <String>[
|
||||
_vswhereMinVersionArgument,
|
||||
_minimumSupportedVersion.toString(),
|
||||
];
|
||||
Map<String, dynamic> visualStudioDetails;
|
||||
Map<String, dynamic>? visualStudioDetails;
|
||||
// Check in the order of stable VS, stable BT, pre-release VS, pre-release BT
|
||||
for (final bool checkForPrerelease in <bool>[false, true]) {
|
||||
for (final String requiredWorkload in _requiredWorkloads) {
|
||||
@ -372,7 +378,7 @@ class VisualStudio {
|
||||
}
|
||||
}
|
||||
_cachedUsableVisualStudioDetails ??= <String, dynamic>{};
|
||||
return _cachedUsableVisualStudioDetails;
|
||||
return _cachedUsableVisualStudioDetails!;
|
||||
}
|
||||
|
||||
/// Returns the details dictionary of the latest version of Visual Studio,
|
||||
@ -382,14 +388,14 @@ class VisualStudio {
|
||||
/// If no installation is found, the cached VS details are set to an empty map
|
||||
/// to avoid repeating vswhere queries that have already not found an
|
||||
/// installation.
|
||||
Map<String, dynamic> _cachedAnyVisualStudioDetails;
|
||||
Map<String, dynamic>? _cachedAnyVisualStudioDetails;
|
||||
Map<String, dynamic> get _anyVisualStudioDetails {
|
||||
// Search for all types of installations.
|
||||
_cachedAnyVisualStudioDetails ??= _visualStudioDetails(
|
||||
additionalArguments: <String>[_vswherePrereleaseArgument, '-all']);
|
||||
// Add a sentinel empty value to avoid querying vswhere again.
|
||||
_cachedAnyVisualStudioDetails ??= <String, dynamic>{};
|
||||
return _cachedAnyVisualStudioDetails;
|
||||
return _cachedAnyVisualStudioDetails!;
|
||||
}
|
||||
|
||||
/// Returns the details dictionary of the best available version of Visual
|
||||
@ -406,7 +412,7 @@ class VisualStudio {
|
||||
|
||||
/// Returns the installation location of the Windows 10 SDKs, or null if the
|
||||
/// registry doesn't contain that information.
|
||||
String _getWindows10SdkLocation() {
|
||||
String? _getWindows10SdkLocation() {
|
||||
try {
|
||||
final RunResult result = _processUtils.runSync(<String>[
|
||||
'reg',
|
||||
@ -417,9 +423,9 @@ class VisualStudio {
|
||||
]);
|
||||
if (result.exitCode == 0) {
|
||||
final RegExp pattern = RegExp(r'InstallationFolder\s+REG_SZ\s+(.+)');
|
||||
final RegExpMatch match = pattern.firstMatch(result.stdout);
|
||||
final RegExpMatch? match = pattern.firstMatch(result.stdout);
|
||||
if (match != null) {
|
||||
return match.group(1).trim();
|
||||
return match.group(1)!.trim();
|
||||
}
|
||||
}
|
||||
} on ArgumentError {
|
||||
@ -434,21 +440,21 @@ class VisualStudio {
|
||||
/// Windows 10 SDK installation directory.
|
||||
///
|
||||
/// Returns null if no Windows 10 SDKs are found.
|
||||
String findHighestVersionInSdkDirectory(Directory dir) {
|
||||
String? findHighestVersionInSdkDirectory(Directory dir) {
|
||||
// This contains subfolders that are named by the SDK version.
|
||||
final Directory includeDir = dir.childDirectory('Includes');
|
||||
if (!includeDir.existsSync()) {
|
||||
return null;
|
||||
}
|
||||
Version highestVersion;
|
||||
Version? highestVersion;
|
||||
for (final FileSystemEntity versionEntry in includeDir.listSync()) {
|
||||
if (!versionEntry.basename.startsWith('10.')) {
|
||||
continue;
|
||||
}
|
||||
// Version only handles 3 components; strip off the '10.' to leave three
|
||||
// components, since they all start with that.
|
||||
final Version version = Version.parse(versionEntry.basename.substring(3));
|
||||
if (highestVersion == null || version > highestVersion) {
|
||||
final Version? version = Version.parse(versionEntry.basename.substring(3));
|
||||
if (highestVersion == null || (version != null && version > highestVersion)) {
|
||||
highestVersion = version;
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,6 +342,18 @@ void setNoViableToolchainInstallation(
|
||||
|
||||
void main() {
|
||||
group('Visual Studio', () {
|
||||
testWithoutContext('isInstalled throws when PROGRAMFILES(X86) env not set', () {
|
||||
final VisualStudio visualStudio = VisualStudio(
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: MemoryFileSystem.test(style: FileSystemStyle.windows),
|
||||
platform: FakePlatform(operatingSystem: 'windows'),
|
||||
processManager: FakeProcessManager.any(),
|
||||
);
|
||||
|
||||
expect(() => visualStudio.isInstalled,
|
||||
throwsToolExit(message: '%PROGRAMFILES(X86)% environment variable not found'));
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalled and cmakePath correct when vswhere is missing', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
|
||||
const Exception exception = ProcessException('vswhere', <String>[]);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user