@@ -10,6 +10,7 @@ import '../base/io.dart';
10
10
import '../base/logger.dart' ;
11
11
import '../base/platform.dart' ;
12
12
import '../base/process.dart' ;
13
+ import '../base/version.dart' ;
13
14
import '../convert.dart' ;
14
15
15
16
/// Encapsulates information about the installed copy of Visual Studio, if any.
@@ -99,6 +100,37 @@ class VisualStudio {
99
100
/// The name of the recommended Visual Studio installer workload.
100
101
String get workloadDescription => 'Desktop development with C++' ;
101
102
103
+ /// Returns the highest installed Windows 10 SDK version, or null if none is
104
+ /// found.
105
+ ///
106
+ /// For instance: 10.0.18362.0
107
+ String getWindows10SDKVersion () {
108
+ final String sdkLocation = _getWindows10SdkLocation ();
109
+ if (sdkLocation == null ) {
110
+ return null ;
111
+ }
112
+ final Directory sdkIncludeDirectory = _fileSystem.directory (sdkLocation).childDirectory ('Include' );
113
+ if (! sdkIncludeDirectory.existsSync ()) {
114
+ return null ;
115
+ }
116
+ // The directories in this folder are named by the SDK version.
117
+ Version highestVersion;
118
+ for (final FileSystemEntity versionEntry in sdkIncludeDirectory.listSync ()) {
119
+ if (versionEntry.basename.startsWith ('10.' )) {
120
+ // Version only handles 3 components; strip off the '10.' to leave three
121
+ // components, since they all start with that.
122
+ final Version version = Version .parse (versionEntry.basename.substring (3 ));
123
+ if (highestVersion == null || version > highestVersion) {
124
+ highestVersion = version;
125
+ }
126
+ }
127
+ }
128
+ if (highestVersion == null ) {
129
+ return null ;
130
+ }
131
+ return '10.$highestVersion ' ;
132
+ }
133
+
102
134
/// The names of the components within the workload that must be installed.
103
135
///
104
136
/// The descriptions of some components differ from version to version. When
@@ -147,6 +179,11 @@ class VisualStudio {
147
179
'vswhere.exe' ,
148
180
);
149
181
182
+ /// Workload ID for use with vswhere requirements.
183
+ ///
184
+ /// See https://docs.microsoft.com/en-us/visualstudio/install/workload-and-component-ids
185
+ static const String _requiredWorkload = 'Microsoft.VisualStudio.Workload.NativeDesktop' ;
186
+
150
187
/// Components for use with vswhere requirements.
151
188
///
152
189
/// Maps from component IDs to description in the installer UI.
@@ -170,14 +207,11 @@ class VisualStudio {
170
207
// wrong after each VC++ toolchain update, so just instruct people to install the
171
208
// latest version.
172
209
cppToolchainDescription += '\n - If there are multiple build tool versions available, install the latest' ;
210
+ // Things which are required by the workload (e.g., MSBuild) don't need to
211
+ // be included here.
173
212
return < String , String > {
174
- // The MSBuild tool and related command-line toolchain.
175
- 'Microsoft.Component.MSBuild' : 'MSBuild' ,
176
213
// The C++ toolchain required by the template.
177
214
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' : cppToolchainDescription,
178
- // The Windows SDK version used by the template.
179
- 'Microsoft.VisualStudio.Component.Windows10SDK.17763' :
180
- 'Windows 10 SDK (10.0.17763.0)' ,
181
215
};
182
216
}
183
217
@@ -217,13 +251,27 @@ class VisualStudio {
217
251
/// This key is under the 'catalog' entry.
218
252
static const String _catalogDisplayVersionKey = 'productDisplayVersion' ;
219
253
220
- /// Returns the details dictionary for the newest version of Visual Studio
221
- /// that includes all of [requiredComponents] , if there is one.
222
- Map <String , dynamic > _visualStudioDetails (
223
- {Iterable <String > requiredComponents, List <String > additionalArguments}) {
224
- final List <String > requirementArguments = requiredComponents == null
225
- ? < String > []
226
- : < String > ['-requires' , ...requiredComponents];
254
+ /// The registry path for Windows 10 SDK installation details.
255
+ static const String _windows10SdkRegistryPath = r'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0' ;
256
+
257
+ /// The registry key in _windows10SdkRegistryPath for the folder where the
258
+ /// SDKs are installed.
259
+ static const String _windows10SdkRegistryKey = 'InstallationFolder' ;
260
+
261
+ /// Returns the details dictionary for the newest version of Visual Studio.
262
+ /// If [validateRequirements] is set, the search will be limited to versions
263
+ /// that have all of the required workloads and components.
264
+ Map <String , dynamic > _visualStudioDetails ({
265
+ bool validateRequirements = false ,
266
+ List <String > additionalArguments,
267
+ }) {
268
+ final List <String > requirementArguments = validateRequirements
269
+ ? < String > [
270
+ '-requires' ,
271
+ _requiredWorkload,
272
+ ..._requiredComponents (_minimumSupportedVersion).keys
273
+ ]
274
+ : < String > [];
227
275
try {
228
276
final List <String > defaultArguments = < String > [
229
277
'-format' , 'json' ,
@@ -290,11 +338,11 @@ class VisualStudio {
290
338
_minimumSupportedVersion.toString (),
291
339
];
292
340
Map <String , dynamic > visualStudioDetails = _visualStudioDetails (
293
- requiredComponents : _requiredComponents (_minimumSupportedVersion).keys ,
341
+ validateRequirements : true ,
294
342
additionalArguments: minimumVersionArguments);
295
343
// If a stable version is not found, try searching for a pre-release version.
296
344
visualStudioDetails ?? = _visualStudioDetails (
297
- requiredComponents : _requiredComponents (_minimumSupportedVersion).keys ,
345
+ validateRequirements : true ,
298
346
additionalArguments: < String > [...minimumVersionArguments, _vswherePrereleaseArgument]);
299
347
300
348
if (visualStudioDetails != null ) {
@@ -336,4 +384,56 @@ class VisualStudio {
336
384
}
337
385
return _anyVisualStudioDetails;
338
386
}
387
+
388
+ /// Returns the installation location of the Windows 10 SDKs, or null if the
389
+ /// registry doesn't contain that information.
390
+ String _getWindows10SdkLocation () {
391
+ try {
392
+ final RunResult result = _processUtils.runSync (< String > [
393
+ 'reg' ,
394
+ 'query' ,
395
+ _windows10SdkRegistryPath,
396
+ '/v' ,
397
+ _windows10SdkRegistryKey,
398
+ ]);
399
+ if (result.exitCode == 0 ) {
400
+ final RegExp pattern = RegExp (r'InstallationFolder\s+REG_SZ\s+(.+)' );
401
+ final RegExpMatch match = pattern.firstMatch (result.stdout);
402
+ if (match != null ) {
403
+ return match.group (1 ).trim ();
404
+ }
405
+ }
406
+ } on ArgumentError {
407
+ // Thrown if reg somehow doesn't exist; ignore and return null below.
408
+ } on ProcessException {
409
+ // Ignored, return null below.
410
+ }
411
+ return null ;
412
+ }
413
+
414
+ /// Returns the highest-numbered SDK version in [dir] , which should be the
415
+ /// Windows 10 SDK installation directory.
416
+ ///
417
+ /// Returns null if no Windows 10 SDKs are found.
418
+ String findHighestVersionInSdkDirectory (Directory dir) {
419
+ // This contains subfolders that are named by the SDK version.
420
+ final Directory includeDir = dir.childDirectory ('Includes' );
421
+ if (! includeDir.existsSync ()) {
422
+ return null ;
423
+ }
424
+ Version highestVersion;
425
+ for (final FileSystemEntity versionEntry in includeDir.listSync ()) {
426
+ if (! versionEntry.basename.startsWith ('10.' )) {
427
+ continue ;
428
+ }
429
+ // Version only handles 3 components; strip off the '10.' to leave three
430
+ // components, since they all start with that.
431
+ final Version version = Version .parse (versionEntry.basename.substring (3 ));
432
+ if (highestVersion == null || version > highestVersion) {
433
+ highestVersion = version;
434
+ }
435
+ }
436
+ // Re-add the leading '10.' that was removed for comparison.
437
+ return highestVersion == null ? null : '10.$highestVersion ' ;
438
+ }
339
439
}
0 commit comments