Skip to content

Commit 109e3d4

Browse files
authored
feat: improve visual studio detection (#2957)
Detect visual studio installation using the VSSetup module and Get-VSSetupInstance method. It works even in systems with the Constrained language mode of PowerShell.
1 parent 3298731 commit 109e3d4

8 files changed

+1517
-51
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Install tools and configuration manually:
5757

5858
To use the native ARM64 C++ compiler on Windows on ARM, ensure that you have Visual Studio 2022 [17.4 or later](https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/) installed.
5959

60+
It's advised to install following Powershell module: [VSSetup](https://github.com./microsoft/vssetup.powershell) using `Install-Module VSSetup -Scope CurrentUser`.
61+
This will make Visual Studio detection logic to use more flexible and accessible method, avoiding Powershell's `ConstrainedLanguage` mode.
62+
6063
### Configuring Python Dependency
6164

6265
`node-gyp` requires that you have installed a [supported version of Python](https://devguide.python.org/versions/).

lib/find-visualstudio.js

+77-10
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class VisualStudioFinder {
5454
}
5555

5656
const checks = [
57+
() => this.findVisualStudio2017OrNewerUsingSetupModule(),
5758
() => this.findVisualStudio2017OrNewer(),
5859
() => this.findVisualStudio2015(),
5960
() => this.findVisualStudio2013()
@@ -113,6 +114,52 @@ class VisualStudioFinder {
113114
throw new Error('Could not find any Visual Studio installation to use')
114115
}
115116

117+
async findVisualStudio2017OrNewerUsingSetupModule () {
118+
const ps = path.join(process.env.SystemRoot, 'System32',
119+
'WindowsPowerShell', 'v1.0', 'powershell.exe')
120+
const vcInstallDir = this.envVcInstallDir
121+
122+
const checkModuleArgs = [
123+
'-NoProfile',
124+
'-Command',
125+
'&{@(Get-Module -ListAvailable -Name VSSetup).Version.ToString()}'
126+
]
127+
this.log.silly('Running', ps, checkModuleArgs)
128+
const [cErr] = await this.execFile(ps, checkModuleArgs)
129+
if (cErr) {
130+
this.addLog('VSSetup module doesn\'t seem to exist. You can install it via: "Install-Module VSSetup -Scope CurrentUser"')
131+
this.log.silly('VSSetup error = %j', cErr && (cErr.stack || cErr))
132+
return null
133+
}
134+
const filterArg = vcInstallDir !== undefined ? `| where {$_.InstallationPath -eq '${vcInstallDir}' }` : ''
135+
const psArgs = [
136+
'-NoProfile',
137+
'-Command',
138+
`&{Get-VSSetupInstance ${filterArg} | ConvertTo-Json -Depth 3}`
139+
]
140+
141+
this.log.silly('Running', ps, psArgs)
142+
const [err, stdout, stderr] = await this.execFile(ps, psArgs)
143+
let parsedData = this.parseData(err, stdout, stderr)
144+
if (parsedData === null) {
145+
return null
146+
}
147+
this.log.silly('Parsed data', parsedData)
148+
if (!Array.isArray(parsedData)) {
149+
// if there are only 1 result, then Powershell will output non-array
150+
parsedData = [parsedData]
151+
}
152+
// normalize output
153+
parsedData = parsedData.map((info) => {
154+
info.path = info.InstallationPath
155+
info.version = `${info.InstallationVersion.Major}.${info.InstallationVersion.Minor}.${info.InstallationVersion.Build}.${info.InstallationVersion.Revision}`
156+
info.packages = info.Packages.map((p) => p.Id)
157+
return info
158+
})
159+
// pass for further processing
160+
return this.processData(parsedData)
161+
}
162+
116163
// Invoke the PowerShell script to get information about Visual Studio 2017
117164
// or newer installations
118165
async findVisualStudio2017OrNewer () {
@@ -128,24 +175,35 @@ class VisualStudioFinder {
128175
]
129176

130177
this.log.silly('Running', ps, psArgs)
131-
const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' })
132-
return this.parseData(err, stdout, stderr)
178+
const [err, stdout, stderr] = await this.execFile(ps, psArgs)
179+
const parsedData = this.parseData(err, stdout, stderr, { checkIsArray: true })
180+
if (parsedData === null) {
181+
return null
182+
}
183+
return this.processData(parsedData)
133184
}
134185

135-
// Parse the output of the PowerShell script and look for an installation
136-
// of Visual Studio 2017 or newer to use
137-
parseData (err, stdout, stderr) {
186+
// Parse the output of the PowerShell script, make sanity checks
187+
parseData (err, stdout, stderr, sanityCheckOptions) {
188+
const defaultOptions = {
189+
checkIsArray: false
190+
}
191+
192+
// Merging provided options with the default options
193+
const sanityOptions = { ...defaultOptions, ...sanityCheckOptions }
194+
138195
this.log.silly('PS stderr = %j', stderr)
139196

140-
const failPowershell = () => {
197+
const failPowershell = (failureDetails) => {
141198
this.addLog(
142-
'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details')
199+
`could not use PowerShell to find Visual Studio 2017 or newer, try re-running with '--loglevel silly' for more details. \n
200+
Failure details: ${failureDetails}`)
143201
return null
144202
}
145203

146204
if (err) {
147205
this.log.silly('PS err = %j', err && (err.stack || err))
148-
return failPowershell()
206+
return failPowershell(`${err}`.substring(0, 40))
149207
}
150208

151209
let vsInfo
@@ -157,11 +215,16 @@ class VisualStudioFinder {
157215
return failPowershell()
158216
}
159217

160-
if (!Array.isArray(vsInfo)) {
218+
if (sanityOptions.checkIsArray && !Array.isArray(vsInfo)) {
161219
this.log.silly('PS stdout = %j', stdout)
162-
return failPowershell()
220+
return failPowershell('Expected array as output of the PS script')
163221
}
222+
return vsInfo
223+
}
164224

225+
// Process parsed data containing information about VS installations
226+
// Look for the required parts, extract and output them back
227+
processData (vsInfo) {
165228
vsInfo = vsInfo.map((info) => {
166229
this.log.silly(`processing installation: "${info.path}"`)
167230
info.path = path.resolve(info.path)
@@ -438,6 +501,10 @@ class VisualStudioFinder {
438501

439502
return true
440503
}
504+
505+
async execFile (exec, args) {
506+
return await execFile(exec, args, { encoding: 'utf8' })
507+
}
441508
}
442509

443510
module.exports = VisualStudioFinder
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
[
2+
{
3+
"InstanceId": "2619cf21",
4+
"InstallationName": "VisualStudio/16.11.33+34407.143",
5+
"InstallationPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional",
6+
"InstallationVersion": {
7+
"Major": 16,
8+
"Minor": 11,
9+
"Build": 34407,
10+
"Revision": 143,
11+
"MajorRevision": 0,
12+
"MinorRevision": 143
13+
},
14+
"InstallDate": "\/Date(1706804943503)\/",
15+
"State": 4294967295,
16+
"DisplayName": "Visual Studio Professional 2019",
17+
"Description": "Professional IDE best suited to small teams",
18+
"ProductPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\Common7\\IDE\\devenv.exe",
19+
"Product": {
20+
"Id": "Microsoft.VisualStudio.Product.Professional",
21+
"Version": {
22+
"Major": 16,
23+
"Minor": 11,
24+
"Build": 34407,
25+
"Revision": 143,
26+
"MajorRevision": 0,
27+
"MinorRevision": 143
28+
},
29+
"Chip": null,
30+
"Branch": null,
31+
"Type": "Product",
32+
"IsExtension": false,
33+
"UniqueId": "Microsoft.VisualStudio.Product.Professional,version=16.11.34407.143"
34+
},
35+
"Packages": [
36+
{
37+
"Id": "Microsoft.VisualStudio.Product.Professional",
38+
"Version": "16.11.34407.143",
39+
"Chip": null,
40+
"Branch": null,
41+
"Type": "Product",
42+
"IsExtension": false,
43+
"UniqueId": "Microsoft.VisualStudio.Product.Professional,version=16.11.34407.143"
44+
},
45+
{
46+
"Id": "Microsoft.VisualStudio.Component.VC.14.29.16.10.ATL",
47+
"Version": "16.11.31314.313",
48+
"Chip": null,
49+
"Branch": null,
50+
"Type": "Component",
51+
"IsExtension": false,
52+
"UniqueId": "Microsoft.VisualStudio.Component.VC.14.29.16.10.ATL,version=16.11.31314.313"
53+
},
54+
{
55+
"Id": "Microsoft.VisualStudio.VC.MSBuild.X64.v142",
56+
"Version": "16.11.31503.54",
57+
"Chip": null,
58+
"Branch": null,
59+
"Type": "Vsix",
60+
"IsExtension": false,
61+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X64.v142,version=16.11.31503.54"
62+
},
63+
{
64+
"Id": "Microsoft.VisualStudio.VC.MSBuild.X64",
65+
"Version": "16.11.31503.54",
66+
"Chip": null,
67+
"Branch": null,
68+
"Type": "Vsix",
69+
"IsExtension": false,
70+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X64,version=16.11.31503.54"
71+
},
72+
{
73+
"Id": "Microsoft.VisualStudio.VC.MSBuild.x86.v142",
74+
"Version": "16.11.31503.54",
75+
"Chip": null,
76+
"Branch": null,
77+
"Type": "Vsix",
78+
"IsExtension": false,
79+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.x86.v142,version=16.11.31503.54"
80+
},
81+
{
82+
"Id": "Microsoft.VisualStudio.VC.MSBuild.X86",
83+
"Version": "16.11.31503.54",
84+
"Chip": null,
85+
"Branch": null,
86+
"Type": "Vsix",
87+
"IsExtension": false,
88+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.X86,version=16.11.31503.54"
89+
},
90+
{
91+
"Id": "Microsoft.VisualStudio.VC.MSBuild.Base",
92+
"Version": "16.11.31829.152",
93+
"Chip": null,
94+
"Branch": null,
95+
"Type": "Vsix",
96+
"IsExtension": false,
97+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.Base,version=16.11.31829.152"
98+
},
99+
{
100+
"Id": "Microsoft.VisualStudio.VC.MSBuild.Base.Resources",
101+
"Version": "16.11.31829.152",
102+
"Chip": null,
103+
"Branch": null,
104+
"Type": "Vsix",
105+
"IsExtension": false,
106+
"UniqueId": "Microsoft.VisualStudio.VC.MSBuild.Base.Resources,version=16.11.31829.152,language=en-US"
107+
},
108+
{
109+
"Id": "Microsoft.VisualStudio.Branding.Professional",
110+
"Version": "16.11.31605.320",
111+
"Chip": null,
112+
"Branch": null,
113+
"Type": "Vsix",
114+
"IsExtension": false,
115+
"UniqueId": "Microsoft.VisualStudio.Branding.Professional,version=16.11.31605.320,language=en-US"
116+
},
117+
{
118+
"Id": "Microsoft.VisualStudio.Component.Windows10SDK.19041",
119+
"Version": "16.10.31205.252",
120+
"Chip": null,
121+
"Branch": null,
122+
"Type": "Component",
123+
"IsExtension": false,
124+
"UniqueId": "Microsoft.VisualStudio.Component.Windows10SDK.19041,version=16.10.31205.252"
125+
},
126+
{
127+
"Id": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
128+
"Version": "16.11.32406.258",
129+
"Chip": null,
130+
"Branch": null,
131+
"Type": "Component",
132+
"IsExtension": false,
133+
"UniqueId": "Microsoft.VisualStudio.Component.VC.Tools.x86.x64,version=16.11.32406.258"
134+
}
135+
],
136+
"Properties": [
137+
{
138+
"Key": "CampaignId",
139+
"Value": "09"
140+
},
141+
{
142+
"Key": "SetupEngineFilePath",
143+
"Value": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\setup.exe"
144+
},
145+
{
146+
"Key": "Nickname",
147+
"Value": ""
148+
},
149+
{
150+
"Key": "ChannelManifestId",
151+
"Value": "VisualStudio.16.Release/16.11.33+34407.143"
152+
}
153+
],
154+
"Errors": null,
155+
"EnginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service",
156+
"IsComplete": true,
157+
"IsLaunchable": true,
158+
"CatalogInfo": [
159+
{
160+
"Key": "Id",
161+
"Value": "VisualStudio/16.11.33+34407.143"
162+
},
163+
{
164+
"Key": "BuildBranch",
165+
"Value": "d16.11"
166+
},
167+
{
168+
"Key": "BuildVersion",
169+
"Value": "16.11.34407.143"
170+
},
171+
{
172+
"Key": "LocalBuild",
173+
"Value": "build-lab"
174+
},
175+
{
176+
"Key": "ManifestName",
177+
"Value": "VisualStudio"
178+
},
179+
{
180+
"Key": "ManifestType",
181+
"Value": "installer"
182+
},
183+
{
184+
"Key": "ProductDisplayVersion",
185+
"Value": "16.11.33"
186+
},
187+
{
188+
"Key": "ProductLine",
189+
"Value": "Dev16"
190+
},
191+
{
192+
"Key": "ProductLineVersion",
193+
"Value": "2019"
194+
},
195+
{
196+
"Key": "ProductMilestone",
197+
"Value": "RTW"
198+
},
199+
{
200+
"Key": "ProductMilestoneIsPreRelease",
201+
"Value": "False"
202+
},
203+
{
204+
"Key": "ProductName",
205+
"Value": "Visual Studio"
206+
},
207+
{
208+
"Key": "ProductPatchVersion",
209+
"Value": "33"
210+
},
211+
{
212+
"Key": "ProductPreReleaseMilestoneSuffix",
213+
"Value": "1.0"
214+
},
215+
{
216+
"Key": "ProductSemanticVersion",
217+
"Value": "16.11.33+34407.143"
218+
},
219+
{
220+
"Key": "RequiredEngineVersion",
221+
"Value": "2.11.72.18200"
222+
}
223+
],
224+
"IsPrerelease": false,
225+
"PSPath": "Microsoft.PowerShell.Core\\FileSystem::C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise",
226+
"UpdateDate": "2024-01-09T19:19:11.0115234Z",
227+
"ResolvedInstallationPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise",
228+
"ChannelId": "VisualStudio.17.Release",
229+
"InstalledChannelId": "VisualStudio.17.Release",
230+
"ChannelUri": "https://aka.ms/vs/17/release/channel",
231+
"InstalledChannelUri": "https://aka.ms/vs/17/release/channel",
232+
"ReleaseNotes": "https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.8#17.8.4",
233+
"ThirdPartyNotices": "https://go.microsoft.com/fwlink/?LinkId=661288"
234+
}
235+
]

0 commit comments

Comments
 (0)