blob: 60b693b6f63bfbf096e6a6d95b22af10ca310637 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../convert.dart';
VisualStudio get visualStudio => context.get<VisualStudio>();
/// Encapsulates information about the installed copy of Visual Studio, if any.
class VisualStudio {
/// True if a sufficiently recent version of Visual Studio is installed.
///
/// Versions older than 2017 Update 2 won't be detected, so error messages to
/// users should take into account that [false] may mean that the user may
/// have an old version rather than no installation at all.
bool get isInstalled => _bestVisualStudioDetails != null;
/// True if there is a version of Visual Studio with all the components
/// necessary to build the project.
bool get hasNecessaryComponents => _usableVisualStudioDetails != null;
/// The name of the Visual Studio install.
///
/// For instance: "Visual Studio Community 2017".
String get displayName => _bestVisualStudioDetails[_displayNameKey];
/// The user-friendly version number of the Visual Studio install.
///
/// For instance: "15.4.0".
String get displayVersion =>
_bestVisualStudioDetails[_catalogKey][_catalogDisplayVersionKey];
/// The directory where Visual Studio is installed.
String get installLocation => _bestVisualStudioDetails[_installationPathKey];
/// The full version of the Visual Studio install.
///
/// For instance: "15.4.27004.2002".
String get fullVersion => _bestVisualStudioDetails[_fullVersionKey];
// Properties that determine the status of the installation. There might be
// Visual Studio versions that don't include them, so default to a "valid" value to
// avoid false negatives.
/// True there is complete installation of Visual Studio.
bool get isComplete => _bestVisualStudioDetails[_isCompleteKey] ?? false;
/// True if Visual Studio is launchable.
bool get isLaunchable => _bestVisualStudioDetails[_isLaunchableKey] ?? false;
/// True if the Visual Studio installation is as pre-release version.
bool get isPrerelease => _bestVisualStudioDetails[_isPrereleaseKey] ?? false;
/// True if a reboot is required to complete the Visual Studio installation.
bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey] ?? false;
/// The name of the recommended Visual Studio installer workload.
String get workloadDescription => 'Desktop development with C++';
/// The names of the components within the workload that must be installed.
///
/// If there is an existing Visual Studio installation, the major version
/// should be provided here, as the descriptions of some componets differ
/// from version to version.
List<String> necessaryComponentDescriptions([int visualStudioMajorVersion]) {
return _requiredComponents(visualStudioMajorVersion).values.toList();
}
/// The path to vcvars64.bat, or null if no Visual Studio installation has
/// the components necessary to build.
String get vcvarsPath {
final Map<String, dynamic> details = _usableVisualStudioDetails;
if (details == null) {
return null;
}
return fs.path.join(
_usableVisualStudioDetails[_installationPathKey],
'VC',
'Auxiliary',
'Build',
'vcvars64.bat',
);
}
/// The path to vswhere.exe.
///
/// vswhere should be installed for VS 2017 Update 2 and later; if it's not
/// 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.
final String _vswherePath = fs.path.join(
platform.environment['PROGRAMFILES(X86)'],
'Microsoft Visual Studio',
'Installer',
'vswhere.exe',
);
/// Components for use with vswhere requriements.
///
/// 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 visualStudioMajorVersion]) {
// The description of the C++ toolchain required by the template. The
// component name is significantly different in different versions.
// Default to the latest known description, but use a specific string
// if a known older version is requested.
String cppToolchainDescription = 'MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.21)';
if (visualStudioMajorVersion == 15) {
cppToolchainDescription = 'VC++ 2017 version 15.9 v14.16 latest v141 tools';
}
return <String, String>{
// The MSBuild tool and related command-line toolchain.
'Microsoft.Component.MSBuild': 'MSBuild',
// The C++ toolchain required by the template.
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64': cppToolchainDescription,
// The Windows SDK version used by the template.
'Microsoft.VisualStudio.Component.Windows10SDK.17763':
'Windows 10 SDK (10.0.17763.0)',
};
}
// Keys in a VS details dictionary returned from vswhere.
/// The root directory of the Visual Studio installation.
static const String _installationPathKey = 'installationPath';
/// The user-friendly name of the installation.
static const String _displayNameKey = 'displayName';
/// The complete version.
static const String _fullVersionKey = 'installationVersion';
/// Keys for the status of the installation.
static const String _isCompleteKey = 'isComplete';
static const String _isLaunchableKey = 'isLaunchable';
static const String _isRebootRequiredKey = 'isRebootRequired';
/// The 'catalog' entry containing more details.
static const String _catalogKey = 'catalog';
/// The key for a pre-release version.
static const String _isPrereleaseKey = 'isPrerelease';
/// The user-friendly version.
///
/// This key is under the 'catalog' entry.
static const String _catalogDisplayVersionKey = 'productDisplayVersion';
/// vswhere argument keys
static const String _prereleaseKey = '-prerelease';
/// Returns the details dictionary for the newest version of Visual Studio
/// that includes all of [requiredComponents], if there is one.
Map<String, dynamic> _visualStudioDetails(
{Iterable<String> requiredComponents, List<String> additionalArguments}) {
final List<String> requirementArguments = requiredComponents == null
? <String>[]
: <String>['-requires', ...requiredComponents];
try {
final List<String> defaultArguments = <String>[
'-format', 'json',
'-utf8',
'-latest',
];
final RunResult whereResult = processUtils.runSync(<String>[
_vswherePath,
...defaultArguments,
...?additionalArguments,
...?requirementArguments,
]);
if (whereResult.exitCode == 0) {
final List<Map<String, dynamic>> installations =
json.decode(whereResult.stdout).cast<Map<String, dynamic>>();
if (installations.isNotEmpty) {
return installations[0];
}
}
} on ArgumentError {
// Thrown if vswhere doesnt' exist; ignore and return null below.
} on ProcessException {
// Ignored, return null below.
}
return null;
}
/// Checks if the given installation has issues that the user must resolve.
///
/// Returns false if the required information is missing since older versions
/// of Visual Studio might not include them.
bool installationHasIssues(Map<String, dynamic>installationDetails) {
assert(installationDetails != null);
if (installationDetails[_isCompleteKey] != null && !installationDetails[_isCompleteKey]) {
return true;
}
if (installationDetails[_isLaunchableKey] != null && !installationDetails[_isLaunchableKey]) {
return true;
}
if (installationDetails[_isRebootRequiredKey] != null && installationDetails[_isRebootRequiredKey]) {
return true;
}
return false;
}
/// Returns the details dictionary for the latest version of Visual Studio
/// that has all required components.
Map<String, dynamic> _cachedUsableVisualStudioDetails;
Map<String, dynamic> get _usableVisualStudioDetails {
_cachedUsableVisualStudioDetails ??=
_visualStudioDetails(requiredComponents: _requiredComponents().keys);
// If a stable version is not found, try searching for a pre-release version.
_cachedUsableVisualStudioDetails ??= _visualStudioDetails(
requiredComponents: _requiredComponents().keys,
additionalArguments: <String>[_prereleaseKey]);
if (_cachedUsableVisualStudioDetails != null) {
if (installationHasIssues(_cachedUsableVisualStudioDetails)) {
_cachedAnyVisualStudioDetails = _cachedUsableVisualStudioDetails;
return null;
}
}
return _cachedUsableVisualStudioDetails;
}
/// Returns the details dictionary of the latest version of Visual Studio,
/// regardless of components.
Map<String, dynamic> _cachedAnyVisualStudioDetails;
Map<String, dynamic> get _anyVisualStudioDetails {
// Search for all types of installations.
_cachedAnyVisualStudioDetails ??= _visualStudioDetails(
additionalArguments: <String>[_prereleaseKey, '-all']);
return _cachedAnyVisualStudioDetails;
}
/// Returns the details dictionary of the best available version of Visual
/// Studio. If there's a version that has all the required components, that
/// will be returned, otherwise returs the lastest installed version (if any).
Map<String, dynamic> get _bestVisualStudioDetails {
if (_usableVisualStudioDetails != null) {
return _usableVisualStudioDetails;
}
return _anyVisualStudioDetails;
}
}