1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import '../base/context.dart'; 6import '../base/file_system.dart'; 7import '../base/io.dart'; 8import '../base/platform.dart'; 9import '../base/process_manager.dart'; 10import '../convert.dart'; 11 12VisualStudio get visualStudio => context.get<VisualStudio>(); 13 14/// Encapsulates information about the installed copy of Visual Studio, if any. 15class VisualStudio { 16 /// True if a sufficiently recent version of Visual Studio is installed. 17 /// 18 /// Versions older than 2017 Update 2 won't be detected, so error messages to 19 /// users should take into account that [false] may mean that the user may 20 /// have an old version rather than no installation at all. 21 bool get isInstalled => _bestVisualStudioDetails != null; 22 23 /// True if there is a version of Visual Studio with all the components 24 /// necessary to build the project. 25 bool get hasNecessaryComponents => _usableVisualStudioDetails != null; 26 27 /// The name of the Visual Studio install. 28 /// 29 /// For instance: "Visual Studio Community 2017". 30 String get displayName => _bestVisualStudioDetails[_displayNameKey]; 31 32 /// The user-friendly version number of the Visual Studio install. 33 /// 34 /// For instance: "15.4.0". 35 String get displayVersion => 36 _bestVisualStudioDetails[_catalogKey][_catalogDisplayVersionKey]; 37 38 /// The directory where Visual Studio is installed. 39 String get installLocation => _bestVisualStudioDetails[_installationPathKey]; 40 41 /// The full version of the Visual Studio install. 42 /// 43 /// For instance: "15.4.27004.2002". 44 String get fullVersion => _bestVisualStudioDetails[_fullVersionKey]; 45 46 /// The name of the recommended Visual Studio installer workload. 47 String get workloadDescription => 'Desktop development with C++'; 48 49 /// The names of the components within the workload that must be installed. 50 /// 51 /// If there is an existing Visual Studio installation, the major version 52 /// should be provided here, as the descriptions of some componets differ 53 /// from version to version. 54 List<String> necessaryComponentDescriptions([int visualStudioMajorVersion]) { 55 return _requiredComponents(visualStudioMajorVersion).values.toList(); 56 } 57 58 /// The path to vcvars64.bat, or null if no Visual Studio installation has 59 /// the components necessary to build. 60 String get vcvarsPath { 61 final Map<String, dynamic> details = _usableVisualStudioDetails; 62 if (details == null) { 63 return null; 64 } 65 return fs.path.join( 66 _usableVisualStudioDetails[_installationPathKey], 67 'VC', 68 'Auxiliary', 69 'Build', 70 'vcvars64.bat', 71 ); 72 } 73 74 /// The path to vswhere.exe. 75 /// 76 /// vswhere should be installed for VS 2017 Update 2 and later; if it's not 77 /// present then there isn't a new enough installation of VS. This path is 78 /// not user-controllable, unlike the install location of Visual Studio 79 /// itself. 80 final String _vswherePath = fs.path.join( 81 platform.environment['PROGRAMFILES(X86)'], 82 'Microsoft Visual Studio', 83 'Installer', 84 'vswhere.exe', 85 ); 86 87 /// Components for use with vswhere requriements. 88 /// 89 /// Maps from component IDs to description in the installer UI. 90 /// See https://docs.microsoft.com/en-us/visualstudio/install/workload-and-component-ids 91 Map<String, String> _requiredComponents([int visualStudioMajorVersion]) { 92 // The description of the C++ toolchain required by the template. The 93 // component name is significantly different in different versions. 94 // Default to the latest known description, but use a specific string 95 // if a known older version is requested. 96 String cppToolchainDescription = 'MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.21)'; 97 if (visualStudioMajorVersion == 15) { 98 cppToolchainDescription = 'VC++ 2017 version 15.9 v14.16 latest v141 tools'; 99 } 100 101 return <String, String>{ 102 // The MSBuild tool and related command-line toolchain. 103 'Microsoft.Component.MSBuild': 'MSBuild', 104 // The C++ toolchain required by the template. 105 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64': cppToolchainDescription, 106 // The Windows SDK version used by the template. 107 'Microsoft.VisualStudio.Component.Windows10SDK.17763': 108 'Windows 10 SDK (10.0.17763.0)', 109 }; 110 } 111 112 // Keys in a VS details dictionary returned from vswhere. 113 114 /// The root directory of the Visual Studio installation. 115 static const String _installationPathKey = 'installationPath'; 116 117 /// The user-friendly name of the installation. 118 static const String _displayNameKey = 'displayName'; 119 120 /// The complete version. 121 static const String _fullVersionKey = 'installationVersion'; 122 123 /// The 'catalog' entry containing more details. 124 static const String _catalogKey = 'catalog'; 125 126 /// The user-friendly version. 127 /// 128 /// This key is under the 'catalog' entry. 129 static const String _catalogDisplayVersionKey = 'productDisplayVersion'; 130 131 /// Returns the details dictionary for the newest version of Visual Studio 132 /// that includes all of [requiredComponents], if there is one. 133 Map<String, dynamic> _visualStudioDetails({Iterable<String> requiredComponents}) { 134 final List<String> requirementArguments = requiredComponents == null 135 ? <String>[] 136 : <String>['-requires', ...requiredComponents]; 137 try { 138 final ProcessResult whereResult = processManager.runSync(<String>[ 139 _vswherePath, 140 '-format', 'json', 141 '-utf8', 142 '-latest', 143 ...?requirementArguments, 144 ]); 145 if (whereResult.exitCode == 0) { 146 final List<Map<String, dynamic>> installations = json.decode(whereResult.stdout) 147 .cast<Map<String, dynamic>>(); 148 if (installations.isNotEmpty) { 149 return installations[0]; 150 } 151 } 152 } on ArgumentError { 153 // Thrown if vswhere doesnt' exist; ignore and return null below. 154 } on ProcessException { 155 // Ignored, return null below. 156 } 157 return null; 158 } 159 160 /// Returns the details dictionary for the latest version of Visual Studio 161 /// that has all required components. 162 Map<String, dynamic> _cachedUsableVisualStudioDetails; 163 Map<String, dynamic> get _usableVisualStudioDetails { 164 _cachedUsableVisualStudioDetails ??= 165 _visualStudioDetails(requiredComponents: _requiredComponents().keys); 166 return _cachedUsableVisualStudioDetails; 167 } 168 169 /// Returns the details dictionary of the latest version of Visual Studio, 170 /// regardless of components. 171 Map<String, dynamic> _cachedAnyVisualStudioDetails; 172 Map<String, dynamic> get _anyVisualStudioDetails { 173 _cachedAnyVisualStudioDetails ??= _visualStudioDetails(); 174 return _cachedAnyVisualStudioDetails; 175 } 176 177 /// Returns the details dictionary of the best available version of Visual 178 /// Studio. If there's a version that has all the required components, that 179 /// will be returned, otherwise returs the lastest installed version (if any). 180 Map<String, dynamic> get _bestVisualStudioDetails { 181 if (_usableVisualStudioDetails != null) { 182 return _usableVisualStudioDetails; 183 } 184 return _anyVisualStudioDetails; 185 } 186} 187