source: trip-planner-front/node_modules/node-gyp/lib/find-visualstudio.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 13.6 KB
RevLine 
[6a3a178]1'use strict'
2
3const log = require('npmlog')
4const execFile = require('child_process').execFile
5const path = require('path').win32
6const logWithPrefix = require('./util').logWithPrefix
7const regSearchKeys = require('./util').regSearchKeys
8
9function findVisualStudio (nodeSemver, configMsvsVersion, callback) {
10 const finder = new VisualStudioFinder(nodeSemver, configMsvsVersion,
11 callback)
12 finder.findVisualStudio()
13}
14
15function VisualStudioFinder (nodeSemver, configMsvsVersion, callback) {
16 this.nodeSemver = nodeSemver
17 this.configMsvsVersion = configMsvsVersion
18 this.callback = callback
19 this.errorLog = []
20 this.validVersions = []
21}
22
23VisualStudioFinder.prototype = {
24 log: logWithPrefix(log, 'find VS'),
25
26 regSearchKeys: regSearchKeys,
27
28 // Logs a message at verbose level, but also saves it to be displayed later
29 // at error level if an error occurs. This should help diagnose the problem.
30 addLog: function addLog (message) {
31 this.log.verbose(message)
32 this.errorLog.push(message)
33 },
34
35 findVisualStudio: function findVisualStudio () {
36 this.configVersionYear = null
37 this.configPath = null
38 if (this.configMsvsVersion) {
39 this.addLog('msvs_version was set from command line or npm config')
40 if (this.configMsvsVersion.match(/^\d{4}$/)) {
41 this.configVersionYear = parseInt(this.configMsvsVersion, 10)
42 this.addLog(
43 `- looking for Visual Studio version ${this.configVersionYear}`)
44 } else {
45 this.configPath = path.resolve(this.configMsvsVersion)
46 this.addLog(
47 `- looking for Visual Studio installed in "${this.configPath}"`)
48 }
49 } else {
50 this.addLog('msvs_version not set from command line or npm config')
51 }
52
53 if (process.env.VCINSTALLDIR) {
54 this.envVcInstallDir =
55 path.resolve(process.env.VCINSTALLDIR, '..')
56 this.addLog('running in VS Command Prompt, installation path is:\n' +
57 `"${this.envVcInstallDir}"\n- will only use this version`)
58 } else {
59 this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt')
60 }
61
62 this.findVisualStudio2017OrNewer((info) => {
63 if (info) {
64 return this.succeed(info)
65 }
66 this.findVisualStudio2015((info) => {
67 if (info) {
68 return this.succeed(info)
69 }
70 this.findVisualStudio2013((info) => {
71 if (info) {
72 return this.succeed(info)
73 }
74 this.fail()
75 })
76 })
77 })
78 },
79
80 succeed: function succeed (info) {
81 this.log.info(`using VS${info.versionYear} (${info.version}) found at:` +
82 `\n"${info.path}"` +
83 '\nrun with --verbose for detailed information')
84 process.nextTick(this.callback.bind(null, null, info))
85 },
86
87 fail: function fail () {
88 if (this.configMsvsVersion && this.envVcInstallDir) {
89 this.errorLog.push(
90 'msvs_version does not match this VS Command Prompt or the',
91 'installation cannot be used.')
92 } else if (this.configMsvsVersion) {
93 // If msvs_version was specified but finding VS failed, print what would
94 // have been accepted
95 this.errorLog.push('')
96 if (this.validVersions) {
97 this.errorLog.push('valid versions for msvs_version:')
98 this.validVersions.forEach((version) => {
99 this.errorLog.push(`- "${version}"`)
100 })
101 } else {
102 this.errorLog.push('no valid versions for msvs_version were found')
103 }
104 }
105
106 const errorLog = this.errorLog.join('\n')
107
108 // For Windows 80 col console, use up to the column before the one marked
109 // with X (total 79 chars including logger prefix, 62 chars usable here):
110 // X
111 const infoLog = [
112 '**************************************************************',
113 'You need to install the latest version of Visual Studio',
114 'including the "Desktop development with C++" workload.',
115 'For more information consult the documentation at:',
116 'https://github.com/nodejs/node-gyp#on-windows',
117 '**************************************************************'
118 ].join('\n')
119
120 this.log.error(`\n${errorLog}\n\n${infoLog}\n`)
121 process.nextTick(this.callback.bind(null, new Error(
122 'Could not find any Visual Studio installation to use')))
123 },
124
125 // Invoke the PowerShell script to get information about Visual Studio 2017
126 // or newer installations
127 findVisualStudio2017OrNewer: function findVisualStudio2017OrNewer (cb) {
128 var ps = path.join(process.env.SystemRoot, 'System32',
129 'WindowsPowerShell', 'v1.0', 'powershell.exe')
130 var csFile = path.join(__dirname, 'Find-VisualStudio.cs')
131 var psArgs = [
132 '-ExecutionPolicy',
133 'Unrestricted',
134 '-NoProfile',
135 '-Command',
136 '&{Add-Type -Path \'' + csFile + '\';' + '[VisualStudioConfiguration.Main]::PrintJson()}'
137 ]
138
139 this.log.silly('Running', ps, psArgs)
140 var child = execFile(ps, psArgs, { encoding: 'utf8' },
141 (err, stdout, stderr) => {
142 this.parseData(err, stdout, stderr, cb)
143 })
144 child.stdin.end()
145 },
146
147 // Parse the output of the PowerShell script and look for an installation
148 // of Visual Studio 2017 or newer to use
149 parseData: function parseData (err, stdout, stderr, cb) {
150 this.log.silly('PS stderr = %j', stderr)
151
152 const failPowershell = () => {
153 this.addLog(
154 'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details')
155 cb(null)
156 }
157
158 if (err) {
159 this.log.silly('PS err = %j', err && (err.stack || err))
160 return failPowershell()
161 }
162
163 var vsInfo
164 try {
165 vsInfo = JSON.parse(stdout)
166 } catch (e) {
167 this.log.silly('PS stdout = %j', stdout)
168 this.log.silly(e)
169 return failPowershell()
170 }
171
172 if (!Array.isArray(vsInfo)) {
173 this.log.silly('PS stdout = %j', stdout)
174 return failPowershell()
175 }
176
177 vsInfo = vsInfo.map((info) => {
178 this.log.silly(`processing installation: "${info.path}"`)
179 info.path = path.resolve(info.path)
180 var ret = this.getVersionInfo(info)
181 ret.path = info.path
182 ret.msBuild = this.getMSBuild(info, ret.versionYear)
183 ret.toolset = this.getToolset(info, ret.versionYear)
184 ret.sdk = this.getSDK(info)
185 return ret
186 })
187 this.log.silly('vsInfo:', vsInfo)
188
189 // Remove future versions or errors parsing version number
190 vsInfo = vsInfo.filter((info) => {
191 if (info.versionYear) {
192 return true
193 }
194 this.addLog(`unknown version "${info.version}" found at "${info.path}"`)
195 return false
196 })
197
198 // Sort to place newer versions first
199 vsInfo.sort((a, b) => b.versionYear - a.versionYear)
200
201 for (var i = 0; i < vsInfo.length; ++i) {
202 const info = vsInfo[i]
203 this.addLog(`checking VS${info.versionYear} (${info.version}) found ` +
204 `at:\n"${info.path}"`)
205
206 if (info.msBuild) {
207 this.addLog('- found "Visual Studio C++ core features"')
208 } else {
209 this.addLog('- "Visual Studio C++ core features" missing')
210 continue
211 }
212
213 if (info.toolset) {
214 this.addLog(`- found VC++ toolset: ${info.toolset}`)
215 } else {
216 this.addLog('- missing any VC++ toolset')
217 continue
218 }
219
220 if (info.sdk) {
221 this.addLog(`- found Windows SDK: ${info.sdk}`)
222 } else {
223 this.addLog('- missing any Windows SDK')
224 continue
225 }
226
227 if (!this.checkConfigVersion(info.versionYear, info.path)) {
228 continue
229 }
230
231 return cb(info)
232 }
233
234 this.addLog(
235 'could not find a version of Visual Studio 2017 or newer to use')
236 cb(null)
237 },
238
239 // Helper - process version information
240 getVersionInfo: function getVersionInfo (info) {
241 const match = /^(\d+)\.(\d+)\..*/.exec(info.version)
242 if (!match) {
243 this.log.silly('- failed to parse version:', info.version)
244 return {}
245 }
246 this.log.silly('- version match = %j', match)
247 var ret = {
248 version: info.version,
249 versionMajor: parseInt(match[1], 10),
250 versionMinor: parseInt(match[2], 10)
251 }
252 if (ret.versionMajor === 15) {
253 ret.versionYear = 2017
254 return ret
255 }
256 if (ret.versionMajor === 16) {
257 ret.versionYear = 2019
258 return ret
259 }
260 this.log.silly('- unsupported version:', ret.versionMajor)
261 return {}
262 },
263
264 // Helper - process MSBuild information
265 getMSBuild: function getMSBuild (info, versionYear) {
266 const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base'
267 if (info.packages.indexOf(pkg) !== -1) {
268 this.log.silly('- found VC.MSBuild.Base')
269 if (versionYear === 2017) {
270 return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe')
271 }
272 if (versionYear === 2019) {
273 return path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe')
274 }
275 }
276 return null
277 },
278
279 // Helper - process toolset information
280 getToolset: function getToolset (info, versionYear) {
281 const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64'
282 const express = 'Microsoft.VisualStudio.WDExpress'
283
284 if (info.packages.indexOf(pkg) !== -1) {
285 this.log.silly('- found VC.Tools.x86.x64')
286 } else if (info.packages.indexOf(express) !== -1) {
287 this.log.silly('- found Visual Studio Express (looking for toolset)')
288 } else {
289 return null
290 }
291
292 if (versionYear === 2017) {
293 return 'v141'
294 } else if (versionYear === 2019) {
295 return 'v142'
296 }
297 this.log.silly('- invalid versionYear:', versionYear)
298 return null
299 },
300
301 // Helper - process Windows SDK information
302 getSDK: function getSDK (info) {
303 const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK'
304 const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.'
305
306 var Win10SDKVer = 0
307 info.packages.forEach((pkg) => {
308 if (!pkg.startsWith(win10SDKPrefix)) {
309 return
310 }
311 const parts = pkg.split('.')
312 if (parts.length > 5 && parts[5] !== 'Desktop') {
313 this.log.silly('- ignoring non-Desktop Win10SDK:', pkg)
314 return
315 }
316 const foundSdkVer = parseInt(parts[4], 10)
317 if (isNaN(foundSdkVer)) {
318 // Microsoft.VisualStudio.Component.Windows10SDK.IpOverUsb
319 this.log.silly('- failed to parse Win10SDK number:', pkg)
320 return
321 }
322 this.log.silly('- found Win10SDK:', foundSdkVer)
323 Win10SDKVer = Math.max(Win10SDKVer, foundSdkVer)
324 })
325
326 if (Win10SDKVer !== 0) {
327 return `10.0.${Win10SDKVer}.0`
328 } else if (info.packages.indexOf(win8SDK) !== -1) {
329 this.log.silly('- found Win8SDK')
330 return '8.1'
331 }
332 return null
333 },
334
335 // Find an installation of Visual Studio 2015 to use
336 findVisualStudio2015: function findVisualStudio2015 (cb) {
337 return this.findOldVS({
338 version: '14.0',
339 versionMajor: 14,
340 versionMinor: 0,
341 versionYear: 2015,
342 toolset: 'v140'
343 }, cb)
344 },
345
346 // Find an installation of Visual Studio 2013 to use
347 findVisualStudio2013: function findVisualStudio2013 (cb) {
348 if (this.nodeSemver.major >= 9) {
349 this.addLog(
350 'not looking for VS2013 as it is only supported up to Node.js 8')
351 return cb(null)
352 }
353 return this.findOldVS({
354 version: '12.0',
355 versionMajor: 12,
356 versionMinor: 0,
357 versionYear: 2013,
358 toolset: 'v120'
359 }, cb)
360 },
361
362 // Helper - common code for VS2013 and VS2015
363 findOldVS: function findOldVS (info, cb) {
364 const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7',
365 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7']
366 const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions'
367
368 this.addLog(`looking for Visual Studio ${info.versionYear}`)
369 this.regSearchKeys(regVC7, info.version, [], (err, res) => {
370 if (err) {
371 this.addLog('- not found')
372 return cb(null)
373 }
374
375 const vsPath = path.resolve(res, '..')
376 this.addLog(`- found in "${vsPath}"`)
377
378 const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32']
379 this.regSearchKeys([`${regMSBuild}\\${info.version}`],
380 'MSBuildToolsPath', msBuildRegOpts, (err, res) => {
381 if (err) {
382 this.addLog(
383 '- could not find MSBuild in registry for this version')
384 return cb(null)
385 }
386
387 const msBuild = path.join(res, 'MSBuild.exe')
388 this.addLog(`- MSBuild in "${msBuild}"`)
389
390 if (!this.checkConfigVersion(info.versionYear, vsPath)) {
391 return cb(null)
392 }
393
394 info.path = vsPath
395 info.msBuild = msBuild
396 info.sdk = null
397 cb(info)
398 })
399 })
400 },
401
402 // After finding a usable version of Visual Stuido:
403 // - add it to validVersions to be displayed at the end if a specific
404 // version was requested and not found;
405 // - check if this is the version that was requested.
406 // - check if this matches the Visual Studio Command Prompt
407 checkConfigVersion: function checkConfigVersion (versionYear, vsPath) {
408 this.validVersions.push(versionYear)
409 this.validVersions.push(vsPath)
410
411 if (this.configVersionYear && this.configVersionYear !== versionYear) {
412 this.addLog('- msvs_version does not match this version')
413 return false
414 }
415 if (this.configPath &&
416 path.relative(this.configPath, vsPath) !== '') {
417 this.addLog('- msvs_version does not point to this installation')
418 return false
419 }
420 if (this.envVcInstallDir &&
421 path.relative(this.envVcInstallDir, vsPath) !== '') {
422 this.addLog('- does not match this Visual Studio Command Prompt')
423 return false
424 }
425
426 return true
427 }
428}
429
430module.exports = findVisualStudio
431module.exports.test = {
432 VisualStudioFinder: VisualStudioFinder,
433 findVisualStudio: findVisualStudio
434}
Note: See TracBrowser for help on using the repository browser.