-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathvsInstalls.ts
More file actions
157 lines (134 loc) · 4.2 KB
/
vsInstalls.ts
File metadata and controls
157 lines (134 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
* @format
*/
import {CodedError} from '@react-native-windows/telemetry';
import {execSync} from 'child_process';
import fs from '@react-native-windows/fs';
import path from 'path';
import semver from 'semver';
/**
* A subset of the per-instance properties returned by vswhere
*/
interface VisualStudioInstallation {
instanceId: string;
installationPath: string;
installationVersion: string;
prerelease: string;
}
/**
* Helper to run vswhere in JSON mode
*
* @param args Arguments to pass to vsWhere
* @param verbose enable verbose logging
*/
function vsWhere(args: string[], verbose?: boolean): any[] {
// This path is maintained and VS has promised to keep it valid.
const vsWherePath = path.join(
process.env['ProgramFiles(x86)'] || process.env.ProgramFiles!,
'/Microsoft Visual Studio/Installer/vswhere.exe',
);
if (verbose) {
console.log('Looking for vswhere at: ' + vsWherePath);
}
if (!fs.existsSync(vsWherePath)) {
throw new CodedError(
'NoVSWhere',
`Unable to find vswhere at ${vsWherePath}`,
);
}
const system32 = `${process.env.SystemRoot}\\System32`;
const cmdline = `${system32}\\cmd.exe /c ${system32}\\chcp.com 65001>nul && "${vsWherePath}" ${args.join(
' ',
)} -format json -utf8`;
const json = JSON.parse(execSync(cmdline).toString());
for (const entry of json) {
entry.prerelease = entry.catalog.productMilestoneIsPreRelease;
}
return json;
}
/**
* Enumerate the installed versions of Visual Studio
*/
export function enumerateVsInstalls(opts: {
requires?: string[];
minVersion?: string;
verbose?: boolean;
latest?: boolean;
prerelease?: boolean;
}): VisualStudioInstallation[] {
const args: string[] = [];
if (opts.minVersion) {
// VS 2019 ex: minVersion == 16.7 => [16.7,17.0)
// VS 2022 ex: minVersion == 17.0 => [17.0,18.0)
// VS 2026 ex: minVersion == 18.0 => [18.0,19.0)
// To support both VS2022 and VS2026 with minVersion == 17.11.0 => [17.11.0,19.0)
// Try to parse minVersion as both a Number and SemVer
const minVersionNum = Number(opts.minVersion);
const minVersionSemVer = semver.parse(opts.minVersion);
let minVersion: string;
let maxVersion: string;
if (minVersionSemVer) {
minVersion = minVersionSemVer.toString();
// Support VS2022 (17.x) and VS2026 (18.x) by extending max version range
// If minVersion is 17.x, allow up to 19.0 to include both VS2022 and VS2026
if (minVersionSemVer.major === 17) {
maxVersion = '19.0';
} else {
maxVersion = `${minVersionSemVer.major + 1}.0`;
}
} else if (!Number.isNaN(minVersionNum)) {
minVersion = Number.isInteger(minVersionNum)
? `${minVersionNum}.0`
: minVersionNum.toString();
// Support VS2022 (17.x) and VS2026 (18.x) by extending max version range
if (Math.floor(minVersionNum) === 17) {
maxVersion = '19.0';
} else {
maxVersion = `${Math.floor(minVersionNum) + 1}.0`;
}
} else {
// Unable to parse minVersion and determine maxVersion,
// caller will throw error that version couldn't be found.
return [];
}
const versionRange =
`[${minVersion},${maxVersion}` + (opts.prerelease ? ']' : ')');
if (opts.verbose) {
console.log(
`Looking for VS installs with version range: ${versionRange}`,
);
}
args.push(`-version ${versionRange}`);
}
if (opts.requires) {
args.push(`-requires ${opts.requires.join(' ')}`);
}
if (opts.latest) {
args.push('-latest');
}
if (opts.prerelease) {
args.push('-prerelease');
}
return vsWhere(args, opts.verbose);
}
/**
* Find the latest available VS installation that matches the given constraints
*/
export function findLatestVsInstall(opts: {
requires?: string[];
minVersion?: string;
verbose?: boolean;
prerelease?: boolean;
}): VisualStudioInstallation | null {
let installs = enumerateVsInstalls({...opts, latest: true});
if (opts.prerelease && installs.length > 0) {
installs = installs.filter(x => x.prerelease === 'True');
}
if (installs.length > 0) {
return installs[0];
} else {
return null;
}
}