mirror of
https://github.com/electron/node-gyp.git
synced 2025-08-15 12:58:19 +02:00
feat: convert all internal functions to async/await
BREAKING CHANGE: All internal functions have been coverted to return promises and no longer accept callbacks. This is not a breaking change for users but may be breaking to consumers of `node-gyp` if you are requiring internal functions directly.
This commit is contained in:
parent
1b3bd341b4
commit
355622f4aa
20 changed files with 768 additions and 1062 deletions
|
@ -68,7 +68,7 @@ if (dir) {
|
|||
}
|
||||
}
|
||||
|
||||
function run () {
|
||||
async function run () {
|
||||
const command = prog.todo.shift()
|
||||
if (!command) {
|
||||
// done!
|
||||
|
@ -77,30 +77,28 @@ function run () {
|
|||
return
|
||||
}
|
||||
|
||||
prog.commands[command.name](command.args, function (err) {
|
||||
if (err) {
|
||||
try {
|
||||
const args = await prog.commands[command.name](command.args) ?? []
|
||||
|
||||
if (command.name === 'list') {
|
||||
if (args.length) {
|
||||
args.forEach((version) => console.log(version))
|
||||
} else {
|
||||
console.log('No node development files installed. Use `node-gyp install` to install a version.')
|
||||
}
|
||||
} else if (args.length >= 1) {
|
||||
console.log(...args.slice(1))
|
||||
}
|
||||
|
||||
// now run the next command in the queue
|
||||
return run()
|
||||
} catch (err) {
|
||||
log.error(command.name + ' error')
|
||||
log.error('stack', err.stack)
|
||||
errorMessage()
|
||||
log.error('not ok')
|
||||
return process.exit(1)
|
||||
}
|
||||
if (command.name === 'list') {
|
||||
const versions = arguments[1]
|
||||
if (versions.length > 0) {
|
||||
versions.forEach(function (version) {
|
||||
console.log(version)
|
||||
})
|
||||
} else {
|
||||
console.log('No node development files installed. Use `node-gyp install` to install a version.')
|
||||
}
|
||||
} else if (arguments.length >= 2) {
|
||||
console.log.apply(console, [].slice.call(arguments, 1))
|
||||
}
|
||||
|
||||
// now run the next command in the queue
|
||||
process.nextTick(run)
|
||||
})
|
||||
}
|
||||
|
||||
process.on('exit', function (code) {
|
||||
|
|
10
lib/build.js
10
lib/build.js
|
@ -202,13 +202,7 @@ async function build (gyp, argv) {
|
|||
await new Promise((resolve, reject) => proc.on('exit', async (code, signal) => {
|
||||
if (buildBinsDir) {
|
||||
// Clean up the build-time dependency symlinks:
|
||||
if (fs.rm) {
|
||||
// fs.rm is only available in Node 14+
|
||||
await fs.rm(buildBinsDir, { recursive: true })
|
||||
} else {
|
||||
// Only used for old node, as recursive rmdir is deprecated in Node 14+
|
||||
await fs.rmdir(buildBinsDir, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
if (code !== 0) {
|
||||
|
@ -222,7 +216,5 @@ async function build (gyp, argv) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = function (gyp, argv, callback) {
|
||||
build(gyp, argv).then(callback.bind(undefined, null), callback)
|
||||
}
|
||||
module.exports = build
|
||||
module.exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs/promises')
|
||||
const fs = require('graceful-fs').promises
|
||||
const log = require('./log')
|
||||
|
||||
async function clean (gyp, argv) {
|
||||
|
@ -11,7 +11,5 @@ async function clean (gyp, argv) {
|
|||
await fs.rm(buildDir, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
module.exports = function (gyp, argv, callback) {
|
||||
clean(gyp, argv).then(callback.bind(undefined, null), callback)
|
||||
}
|
||||
module.exports = clean
|
||||
module.exports.usage = 'Removes any generated build files and the "out" dir'
|
||||
|
|
121
lib/configure.js
121
lib/configure.js
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const { openSync, closeSync, promises: fs } = require('graceful-fs')
|
||||
const path = require('path')
|
||||
const log = require('./log')
|
||||
const os = require('os')
|
||||
|
@ -8,40 +8,28 @@ const processRelease = require('./process-release')
|
|||
const win = process.platform === 'win32'
|
||||
const findNodeDirectory = require('./find-node-directory')
|
||||
const createConfigGypi = require('./create-config-gypi')
|
||||
const msgFormat = require('util').format
|
||||
const { format: msgFormat } = require('util')
|
||||
const findPython = require('./find-python')
|
||||
let findVisualStudio
|
||||
if (win) {
|
||||
findVisualStudio = require('./find-visualstudio')
|
||||
}
|
||||
const findVisualStudio = win ? require('./find-visualstudio') : null
|
||||
|
||||
function configure (gyp, argv, callback) {
|
||||
let python
|
||||
async function configure (gyp, argv) {
|
||||
const buildDir = path.resolve('build')
|
||||
const configNames = ['config.gypi', 'common.gypi']
|
||||
const configs = []
|
||||
let nodeDir
|
||||
const release = processRelease(argv, gyp, process.version, process.release)
|
||||
|
||||
findPython(gyp.opts.python, function (err, found) {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else {
|
||||
python = found
|
||||
getNodeDir()
|
||||
}
|
||||
})
|
||||
const python = await findPython(gyp.opts.python)
|
||||
return getNodeDir()
|
||||
|
||||
function getNodeDir () {
|
||||
async function getNodeDir () {
|
||||
// 'python' should be set by now
|
||||
process.env.PYTHON = python
|
||||
|
||||
if (gyp.opts.nodedir) {
|
||||
// --nodedir was specified. use that for the dev files
|
||||
nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir())
|
||||
|
||||
log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir)
|
||||
createBuildDir()
|
||||
} else {
|
||||
// if no --nodedir specified, ensure node dependencies are installed
|
||||
if ('v' + release.version !== process.version) {
|
||||
|
@ -54,87 +42,66 @@ function configure (gyp, argv, callback) {
|
|||
|
||||
if (!release.semver) {
|
||||
// could not parse the version string with semver
|
||||
return callback(new Error('Invalid version number: ' + release.version))
|
||||
throw new Error('Invalid version number: ' + release.version)
|
||||
}
|
||||
|
||||
// If the tarball option is set, always remove and reinstall the headers
|
||||
// into devdir. Otherwise only install if they're not already there.
|
||||
gyp.opts.ensure = !gyp.opts.tarball
|
||||
|
||||
gyp.commands.install([release.version], function (err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
await gyp.commands.install([release.version])
|
||||
|
||||
log.verbose('get node dir', 'target node version installed:', release.versionDir)
|
||||
nodeDir = path.resolve(gyp.devDir, release.versionDir)
|
||||
createBuildDir()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function createBuildDir () {
|
||||
return createBuildDir()
|
||||
}
|
||||
|
||||
async function createBuildDir () {
|
||||
log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir)
|
||||
|
||||
fs.mkdir(buildDir, { recursive: true }, function (err, isNew) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const isNew = await fs.mkdir(buildDir, { recursive: true })
|
||||
log.verbose(
|
||||
'build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No'
|
||||
)
|
||||
if (win) {
|
||||
findVisualStudio(release.semver, gyp.opts['msvs-version'],
|
||||
createConfigFile)
|
||||
} else {
|
||||
createConfigFile()
|
||||
}
|
||||
})
|
||||
const vsInfo = win ? await findVisualStudio(release.semver, gyp.opts['msvs-version']) : null
|
||||
return createConfigFile(vsInfo)
|
||||
}
|
||||
|
||||
function createConfigFile (err, vsInfo) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
async function createConfigFile (vsInfo) {
|
||||
if (win) {
|
||||
process.env.GYP_MSVS_VERSION = Math.min(vsInfo.versionYear, 2015)
|
||||
process.env.GYP_MSVS_OVERRIDE_PATH = vsInfo.path
|
||||
}
|
||||
createConfigGypi({ gyp, buildDir, nodeDir, vsInfo, python }).then(configPath => {
|
||||
const configPath = await createConfigGypi({ gyp, buildDir, nodeDir, vsInfo, python })
|
||||
configs.push(configPath)
|
||||
findConfigs()
|
||||
}).catch(err => {
|
||||
callback(err)
|
||||
})
|
||||
return findConfigs()
|
||||
}
|
||||
|
||||
function findConfigs () {
|
||||
async function findConfigs () {
|
||||
const name = configNames.shift()
|
||||
if (!name) {
|
||||
return runGyp()
|
||||
}
|
||||
const fullPath = path.resolve(name)
|
||||
|
||||
const fullPath = path.resolve(name)
|
||||
log.verbose(name, 'checking for gypi file: %s', fullPath)
|
||||
fs.stat(fullPath, function (err) {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
findConfigs() // check next gypi filename
|
||||
} else {
|
||||
callback(err)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await fs.stat(fullPath)
|
||||
log.verbose(name, 'found gypi file')
|
||||
configs.push(fullPath)
|
||||
findConfigs()
|
||||
} catch (err) {
|
||||
// ENOENT will check next gypi filename
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function runGyp (err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
return findConfigs()
|
||||
}
|
||||
|
||||
async function runGyp () {
|
||||
if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
|
||||
if (win) {
|
||||
log.verbose('gyp', 'gyp format was not specified; forcing "msvs"')
|
||||
|
@ -189,7 +156,7 @@ function configure (gyp, argv, callback) {
|
|||
} else {
|
||||
const msg = msgFormat('Could not find node.%s file in %s', ext, nodeRootDir)
|
||||
log.error(logprefix, 'Could not find exports file')
|
||||
return callback(new Error(msg))
|
||||
throw new Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +194,7 @@ function configure (gyp, argv, callback) {
|
|||
} else if (release.version.split('.')[0] >= 16) {
|
||||
// zoslib is only shipped in Node v16 and above.
|
||||
log.error(logprefix, msg)
|
||||
return callback(new Error(msg))
|
||||
throw new Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,8 +202,9 @@ function configure (gyp, argv, callback) {
|
|||
const gypScript = path.resolve(__dirname, '..', 'gyp', 'gyp_main.py')
|
||||
const addonGypi = path.resolve(__dirname, '..', 'addon.gypi')
|
||||
let commonGypi = path.resolve(nodeDir, 'include/node/common.gypi')
|
||||
fs.stat(commonGypi, function (err) {
|
||||
if (err) {
|
||||
try {
|
||||
await fs.stat(commonGypi)
|
||||
} catch (err) {
|
||||
commonGypi = path.resolve(nodeDir, 'common.gypi')
|
||||
}
|
||||
|
||||
|
@ -295,18 +263,17 @@ function configure (gyp, argv, callback) {
|
|||
}
|
||||
process.env.PYTHONPATH = pypath.join(win ? ';' : ':')
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const cp = gyp.spawn(python, argv)
|
||||
cp.on('exit', onCpExit)
|
||||
})
|
||||
}
|
||||
|
||||
function onCpExit (code) {
|
||||
cp.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
callback(new Error('`gyp` failed with exit code: ' + code))
|
||||
reject(new Error('`gyp` failed with exit code: ' + code))
|
||||
} else {
|
||||
// we're done
|
||||
callback()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,13 +287,13 @@ function findAccessibleSync (logprefix, dir, candidates) {
|
|||
const candidate = path.resolve(dir, candidates[next])
|
||||
let fd
|
||||
try {
|
||||
fd = fs.openSync(candidate, 'r')
|
||||
fd = openSync(candidate, 'r')
|
||||
} catch (e) {
|
||||
// this candidate was not found or not readable, do nothing
|
||||
log.silly(logprefix, 'Could not open %s: %s', candidate, e.message)
|
||||
continue
|
||||
}
|
||||
fs.closeSync(fd)
|
||||
closeSync(fd)
|
||||
log.silly(logprefix, 'Found readable %s', candidate)
|
||||
return candidate
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const fs = require('graceful-fs').promises
|
||||
const log = require('./log')
|
||||
const path = require('path')
|
||||
|
||||
|
@ -24,7 +24,7 @@ async function getBaseConfigGypi ({ gyp, nodeDir }) {
|
|||
if (shouldReadConfigGypi && nodeDir) {
|
||||
try {
|
||||
const baseConfigGypiPath = path.resolve(nodeDir, 'include/node/config.gypi')
|
||||
const baseConfigGypi = await fs.promises.readFile(baseConfigGypiPath)
|
||||
const baseConfigGypi = await fs.readFile(baseConfigGypiPath)
|
||||
return parseConfigGypi(baseConfigGypi.toString())
|
||||
} catch (err) {
|
||||
log.warn('read config.gypi', err.message)
|
||||
|
@ -138,7 +138,7 @@ async function createConfigGypi ({ gyp, buildDir, nodeDir, vsInfo, python }) {
|
|||
|
||||
const json = JSON.stringify(config, boolsToString, 2)
|
||||
log.verbose('build/' + configFilename, 'writing out config file: %s', configPath)
|
||||
await fs.promises.writeFile(configPath, [prefix, json, ''].join('\n'))
|
||||
await fs.writeFile(configPath, [prefix, json, ''].join('\n'))
|
||||
|
||||
return configPath
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
const log = require('./log')
|
||||
const semver = require('semver')
|
||||
const cp = require('child_process')
|
||||
const extend = require('util')._extend // eslint-disable-line
|
||||
const { _extend: extend } = require('util') // eslint-disable-line n/no-deprecated-api
|
||||
const { execFile } = require('./util')
|
||||
const win = process.platform === 'win32'
|
||||
|
||||
const systemDrive = process.env.SystemDrive || 'C:'
|
||||
|
@ -38,8 +38,7 @@ function getOsUserInfo () {
|
|||
} catch (e) {}
|
||||
}
|
||||
|
||||
function PythonFinder (configPython, callback) {
|
||||
this.callback = callback
|
||||
function PythonFinder (configPython) {
|
||||
this.configPython = configPython
|
||||
this.errorLog = []
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ PythonFinder.prototype = {
|
|||
semverRange: '>=3.6.0',
|
||||
|
||||
// These can be overridden for testing:
|
||||
execFile: cp.execFile,
|
||||
execFile,
|
||||
env: process.env,
|
||||
win,
|
||||
pyLauncher: 'py.exe',
|
||||
|
@ -66,11 +65,10 @@ PythonFinder.prototype = {
|
|||
|
||||
// Find Python by trying a sequence of possibilities.
|
||||
// Ignore errors, keep trying until Python is found.
|
||||
findPython: function findPython () {
|
||||
const SKIP = 0; const FAIL = 1
|
||||
const toCheck = getChecks.apply(this)
|
||||
|
||||
function getChecks () {
|
||||
findPython: async function findPython () {
|
||||
const SKIP = 0
|
||||
const FAIL = 1
|
||||
const toCheck = (() => {
|
||||
if (this.env.NODE_GYP_FORCE_PYTHON) {
|
||||
return [{
|
||||
before: () => {
|
||||
|
@ -79,8 +77,7 @@ PythonFinder.prototype = {
|
|||
this.addLog('- process.env.NODE_GYP_FORCE_PYTHON is ' +
|
||||
`"${this.env.NODE_GYP_FORCE_PYTHON}"`)
|
||||
},
|
||||
check: this.checkCommand,
|
||||
arg: this.env.NODE_GYP_FORCE_PYTHON
|
||||
check: () => this.checkCommand(this.env.NODE_GYP_FORCE_PYTHON)
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -97,8 +94,7 @@ PythonFinder.prototype = {
|
|||
this.addLog('- "--python=" or "npm config get python" is ' +
|
||||
`"${this.configPython}"`)
|
||||
},
|
||||
check: this.checkCommand,
|
||||
arg: this.configPython
|
||||
check: () => this.checkCommand(this.configPython)
|
||||
},
|
||||
{
|
||||
before: () => {
|
||||
|
@ -111,8 +107,7 @@ PythonFinder.prototype = {
|
|||
'variable PYTHON')
|
||||
this.addLog(`- process.env.PYTHON is "${this.env.PYTHON}"`)
|
||||
},
|
||||
check: this.checkCommand,
|
||||
arg: this.env.PYTHON
|
||||
check: () => this.checkCommand(this.env.PYTHON)
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -122,20 +117,18 @@ PythonFinder.prototype = {
|
|||
this.addLog(
|
||||
'checking if the py launcher can be used to find Python 3')
|
||||
},
|
||||
check: this.checkPyLauncher
|
||||
check: () => this.checkPyLauncher()
|
||||
})
|
||||
}
|
||||
|
||||
checks.push(...[
|
||||
{
|
||||
before: () => { this.addLog('checking if "python3" can be used') },
|
||||
check: this.checkCommand,
|
||||
arg: 'python3'
|
||||
check: () => this.checkCommand('python3')
|
||||
},
|
||||
{
|
||||
before: () => { this.addLog('checking if "python" can be used') },
|
||||
check: this.checkCommand,
|
||||
arg: 'python'
|
||||
check: () => this.checkCommand('python')
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -143,49 +136,37 @@ PythonFinder.prototype = {
|
|||
for (let i = 0; i < this.winDefaultLocations.length; ++i) {
|
||||
const location = this.winDefaultLocations[i]
|
||||
checks.push({
|
||||
before: () => {
|
||||
this.addLog('checking if Python is ' +
|
||||
`${location}`)
|
||||
},
|
||||
check: this.checkExecPath,
|
||||
arg: location
|
||||
before: () => this.addLog(`checking if Python is ${location}`),
|
||||
check: () => this.checkExecPath(location)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return checks
|
||||
}
|
||||
})()
|
||||
|
||||
function runChecks (err) {
|
||||
this.log.silly('runChecks: err = %j', (err && err.stack) || err)
|
||||
|
||||
const check = toCheck.shift()
|
||||
if (!check) {
|
||||
return this.fail()
|
||||
}
|
||||
|
||||
const before = check.before.apply(this)
|
||||
for (const check of toCheck) {
|
||||
const before = check.before()
|
||||
if (before === SKIP) {
|
||||
return runChecks.apply(this)
|
||||
continue
|
||||
}
|
||||
if (before === FAIL) {
|
||||
return this.fail()
|
||||
}
|
||||
|
||||
const args = [runChecks.bind(this)]
|
||||
if (check.arg) {
|
||||
args.unshift(check.arg)
|
||||
try {
|
||||
return await check.check()
|
||||
} catch (err) {
|
||||
this.log.silly('runChecks: err = %j', (err && err.stack) || err)
|
||||
}
|
||||
check.check.apply(this, args)
|
||||
}
|
||||
|
||||
runChecks.apply(this)
|
||||
return this.fail()
|
||||
},
|
||||
|
||||
// Check if command is a valid Python to use.
|
||||
// Will exit the Python finder on success.
|
||||
// If on Windows, run in a CMD shell to support BAT/CMD launchers.
|
||||
checkCommand: function checkCommand (command, errorCallback) {
|
||||
checkCommand: async function checkCommand (command) {
|
||||
let exec = command
|
||||
let args = this.argsExecutable
|
||||
let shell = false
|
||||
|
@ -197,18 +178,18 @@ PythonFinder.prototype = {
|
|||
}
|
||||
|
||||
this.log.verbose(`- executing "${command}" to get executable path`)
|
||||
this.run(exec, args, shell, function (err, execPath) {
|
||||
// Possible outcomes:
|
||||
// - Error: not in PATH, not executable or execution fails
|
||||
// - Gibberish: the next command to check version will fail
|
||||
// - Absolute path to executable
|
||||
if (err) {
|
||||
this.addLog(`- "${command}" is not in PATH or produced an error`)
|
||||
return errorCallback(err)
|
||||
}
|
||||
try {
|
||||
const execPath = await this.run(exec, args, shell)
|
||||
this.addLog(`- executable path is "${execPath}"`)
|
||||
this.checkExecPath(execPath, errorCallback)
|
||||
}.bind(this))
|
||||
return this.checkExecPath(execPath)
|
||||
} catch (err) {
|
||||
this.addLog(`- "${command}" is not in PATH or produced an error`)
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
// Check if the py launcher can find a valid Python to use.
|
||||
|
@ -221,37 +202,31 @@ PythonFinder.prototype = {
|
|||
// the first command line argument. Since "py.exe -3" would be an invalid
|
||||
// executable for "execFile", we have to use the launcher to figure out
|
||||
// where the actual "python.exe" executable is located.
|
||||
checkPyLauncher: function checkPyLauncher (errorCallback) {
|
||||
this.log.verbose(
|
||||
`- executing "${this.pyLauncher}" to get Python 3 executable path`)
|
||||
this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false,
|
||||
function (err, execPath) {
|
||||
checkPyLauncher: async function checkPyLauncher () {
|
||||
this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`)
|
||||
// Possible outcomes: same as checkCommand
|
||||
if (err) {
|
||||
this.addLog(
|
||||
`- "${this.pyLauncher}" is not in PATH or produced an error`)
|
||||
return errorCallback(err)
|
||||
}
|
||||
try {
|
||||
const execPath = await this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false)
|
||||
this.addLog(`- executable path is "${execPath}"`)
|
||||
this.checkExecPath(execPath, errorCallback)
|
||||
}.bind(this))
|
||||
return this.checkExecPath(execPath)
|
||||
} catch (err) {
|
||||
this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`)
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
// Check if a Python executable is the correct version to use.
|
||||
// Will exit the Python finder on success.
|
||||
checkExecPath: function checkExecPath (execPath, errorCallback) {
|
||||
checkExecPath: async function checkExecPath (execPath) {
|
||||
this.log.verbose(`- executing "${execPath}" to get version`)
|
||||
this.run(execPath, this.argsVersion, false, function (err, version) {
|
||||
// Possible outcomes:
|
||||
// - Error: executable can not be run (likely meaning the command wasn't
|
||||
// a Python executable and the previous command produced gibberish)
|
||||
// - Gibberish: somehow the last command produced an executable path,
|
||||
// this will fail when verifying the version
|
||||
// - Version of the Python executable
|
||||
if (err) {
|
||||
this.addLog(`- "${execPath}" could not be run`)
|
||||
return errorCallback(err)
|
||||
}
|
||||
try {
|
||||
const version = await this.run(execPath, this.argsVersion, false)
|
||||
this.addLog(`- version is "${version}"`)
|
||||
|
||||
const range = new semver.Range(this.semverRange)
|
||||
|
@ -262,21 +237,22 @@ PythonFinder.prototype = {
|
|||
this.log.silly('range.test() threw:\n%s', err.stack)
|
||||
this.addLog(`- "${execPath}" does not have a valid version`)
|
||||
this.addLog('- is it a Python executable?')
|
||||
return errorCallback(err)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
this.addLog(`- version is ${version} - should be ${this.semverRange}`)
|
||||
this.addLog('- THIS VERSION OF PYTHON IS NOT SUPPORTED')
|
||||
return errorCallback(new Error(
|
||||
`Found unsupported Python version ${version}`))
|
||||
throw new Error(`Found unsupported Python version ${version}`)
|
||||
}
|
||||
return this.succeed(execPath, version)
|
||||
} catch (err) {
|
||||
this.addLog(`- "${execPath}" could not be run`)
|
||||
throw err
|
||||
}
|
||||
this.succeed(execPath, version)
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
// Run an executable or shell command, trimming the output.
|
||||
run: function run (exec, args, shell, callback) {
|
||||
run: async function run (exec, args, shell) {
|
||||
const env = extend({}, this.env)
|
||||
env.TERM = 'dumb'
|
||||
const opts = { env, shell }
|
||||
|
@ -285,27 +261,20 @@ PythonFinder.prototype = {
|
|||
this.log.silly('execFile: args = %j', args)
|
||||
this.log.silly('execFile: opts = %j', opts)
|
||||
try {
|
||||
this.execFile(exec, args, opts, execFileCallback.bind(this))
|
||||
} catch (err) {
|
||||
this.log.silly('execFile: threw:\n%s', err.stack)
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
function execFileCallback (err, stdout, stderr) {
|
||||
const [err, stdout, stderr] = await this.execFile(exec, args, opts)
|
||||
this.log.silly('execFile result: err = %j', (err && err.stack) || err)
|
||||
this.log.silly('execFile result: stdout = %j', stdout)
|
||||
this.log.silly('execFile result: stderr = %j', stderr)
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const execPath = stdout.trim()
|
||||
callback(null, execPath)
|
||||
return stdout.trim()
|
||||
} catch (err) {
|
||||
this.log.silly('execFile: threw:\n%s', err.stack)
|
||||
throw err
|
||||
}
|
||||
},
|
||||
|
||||
succeed: function succeed (execPath, version) {
|
||||
this.log.info(`using Python version ${version} found at "${execPath}"`)
|
||||
process.nextTick(this.callback.bind(null, null, execPath))
|
||||
return execPath
|
||||
},
|
||||
|
||||
fail: function fail () {
|
||||
|
@ -333,15 +302,11 @@ PythonFinder.prototype = {
|
|||
].join('\n')
|
||||
|
||||
this.log.error(`\n${errorLog}\n\n${info}\n`)
|
||||
process.nextTick(this.callback.bind(null, new Error(
|
||||
'Could not find any Python installation to use')))
|
||||
throw new Error('Could not find any Python installation to use')
|
||||
}
|
||||
}
|
||||
|
||||
function findPython (configPython, callback) {
|
||||
const finder = new PythonFinder(configPython, callback)
|
||||
finder.findPython()
|
||||
}
|
||||
const findPython = async (configPython) => new PythonFinder(configPython).findPython()
|
||||
|
||||
module.exports = findPython
|
||||
module.exports.test = {
|
||||
|
|
|
@ -1,21 +1,13 @@
|
|||
'use strict'
|
||||
|
||||
const log = require('./log')
|
||||
const execFile = require('child_process').execFile
|
||||
const fs = require('fs')
|
||||
const path = require('path').win32
|
||||
const regSearchKeys = require('./util').regSearchKeys
|
||||
const { existsSync } = require('fs')
|
||||
const { win32: path } = require('path')
|
||||
const { regSearchKeys, execFile } = require('./util')
|
||||
|
||||
function findVisualStudio (nodeSemver, configMsvsVersion, callback) {
|
||||
const finder = new VisualStudioFinder(nodeSemver, configMsvsVersion,
|
||||
callback)
|
||||
finder.findVisualStudio()
|
||||
}
|
||||
|
||||
function VisualStudioFinder (nodeSemver, configMsvsVersion, callback) {
|
||||
function VisualStudioFinder (nodeSemver, configMsvsVersion) {
|
||||
this.nodeSemver = nodeSemver
|
||||
this.configMsvsVersion = configMsvsVersion
|
||||
this.callback = callback
|
||||
this.errorLog = []
|
||||
this.validVersions = []
|
||||
}
|
||||
|
@ -32,7 +24,7 @@ VisualStudioFinder.prototype = {
|
|||
this.errorLog.push(message)
|
||||
},
|
||||
|
||||
findVisualStudio: function findVisualStudio () {
|
||||
findVisualStudio: async function findVisualStudio () {
|
||||
this.configVersionYear = null
|
||||
this.configPath = null
|
||||
if (this.configMsvsVersion) {
|
||||
|
@ -59,29 +51,27 @@ VisualStudioFinder.prototype = {
|
|||
this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt')
|
||||
}
|
||||
|
||||
this.findVisualStudio2017OrNewer((info) => {
|
||||
const checks = [
|
||||
() => this.findVisualStudio2017OrNewer(),
|
||||
() => this.findVisualStudio2015(),
|
||||
() => this.findVisualStudio2013()
|
||||
]
|
||||
|
||||
for (const check of checks) {
|
||||
const info = await check()
|
||||
if (info) {
|
||||
return this.succeed(info)
|
||||
}
|
||||
this.findVisualStudio2015((info) => {
|
||||
if (info) {
|
||||
return this.succeed(info)
|
||||
}
|
||||
this.findVisualStudio2013((info) => {
|
||||
if (info) {
|
||||
return this.succeed(info)
|
||||
}
|
||||
this.fail()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return this.fail()
|
||||
},
|
||||
|
||||
succeed: function succeed (info) {
|
||||
this.log.info(`using VS${info.versionYear} (${info.version}) found at:` +
|
||||
`\n"${info.path}"` +
|
||||
'\nrun with --verbose for detailed information')
|
||||
process.nextTick(this.callback.bind(null, null, info))
|
||||
return info
|
||||
},
|
||||
|
||||
fail: function fail () {
|
||||
|
@ -118,13 +108,12 @@ VisualStudioFinder.prototype = {
|
|||
].join('\n')
|
||||
|
||||
this.log.error(`\n${errorLog}\n\n${infoLog}\n`)
|
||||
process.nextTick(this.callback.bind(null, new Error(
|
||||
'Could not find any Visual Studio installation to use')))
|
||||
throw new Error('Could not find any Visual Studio installation to use')
|
||||
},
|
||||
|
||||
// Invoke the PowerShell script to get information about Visual Studio 2017
|
||||
// or newer installations
|
||||
findVisualStudio2017OrNewer: function findVisualStudio2017OrNewer (cb) {
|
||||
findVisualStudio2017OrNewer: async function findVisualStudio2017OrNewer () {
|
||||
const ps = path.join(process.env.SystemRoot, 'System32',
|
||||
'WindowsPowerShell', 'v1.0', 'powershell.exe')
|
||||
const csFile = path.join(__dirname, 'Find-VisualStudio.cs')
|
||||
|
@ -137,22 +126,19 @@ VisualStudioFinder.prototype = {
|
|||
]
|
||||
|
||||
this.log.silly('Running', ps, psArgs)
|
||||
const child = execFile(ps, psArgs, { encoding: 'utf8' },
|
||||
(err, stdout, stderr) => {
|
||||
this.parseData(err, stdout, stderr, cb)
|
||||
})
|
||||
child.stdin.end()
|
||||
const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' })
|
||||
return this.parseData(err, stdout, stderr)
|
||||
},
|
||||
|
||||
// Parse the output of the PowerShell script and look for an installation
|
||||
// of Visual Studio 2017 or newer to use
|
||||
parseData: function parseData (err, stdout, stderr, cb) {
|
||||
parseData: function parseData (err, stdout, stderr) {
|
||||
this.log.silly('PS stderr = %j', stderr)
|
||||
|
||||
const failPowershell = () => {
|
||||
this.addLog(
|
||||
'could not use PowerShell to find Visual Studio 2017 or newer, try re-running with \'--loglevel silly\' for more details')
|
||||
cb(null)
|
||||
return null
|
||||
}
|
||||
|
||||
if (err) {
|
||||
|
@ -228,12 +214,12 @@ VisualStudioFinder.prototype = {
|
|||
continue
|
||||
}
|
||||
|
||||
return cb(info)
|
||||
return info
|
||||
}
|
||||
|
||||
this.addLog(
|
||||
'could not find a version of Visual Studio 2017 or newer to use')
|
||||
cb(null)
|
||||
return null
|
||||
},
|
||||
|
||||
// Helper - process version information
|
||||
|
@ -266,7 +252,7 @@ VisualStudioFinder.prototype = {
|
|||
},
|
||||
|
||||
msBuildPathExists: function msBuildPathExists (path) {
|
||||
return fs.existsSync(path)
|
||||
return existsSync(path)
|
||||
},
|
||||
|
||||
// Helper - process MSBuild information
|
||||
|
@ -356,11 +342,11 @@ VisualStudioFinder.prototype = {
|
|||
},
|
||||
|
||||
// Find an installation of Visual Studio 2015 to use
|
||||
findVisualStudio2015: function findVisualStudio2015 (cb) {
|
||||
findVisualStudio2015: async function findVisualStudio2015 () {
|
||||
if (this.nodeSemver.major >= 19) {
|
||||
this.addLog(
|
||||
'not looking for VS2015 as it is only supported up to Node.js 18')
|
||||
return cb(null)
|
||||
return null
|
||||
}
|
||||
return this.findOldVS({
|
||||
version: '14.0',
|
||||
|
@ -368,15 +354,15 @@ VisualStudioFinder.prototype = {
|
|||
versionMinor: 0,
|
||||
versionYear: 2015,
|
||||
toolset: 'v140'
|
||||
}, cb)
|
||||
})
|
||||
},
|
||||
|
||||
// Find an installation of Visual Studio 2013 to use
|
||||
findVisualStudio2013: function findVisualStudio2013 (cb) {
|
||||
findVisualStudio2013: async function findVisualStudio2013 () {
|
||||
if (this.nodeSemver.major >= 9) {
|
||||
this.addLog(
|
||||
'not looking for VS2013 as it is only supported up to Node.js 8')
|
||||
return cb(null)
|
||||
return null
|
||||
}
|
||||
return this.findOldVS({
|
||||
version: '12.0',
|
||||
|
@ -384,47 +370,44 @@ VisualStudioFinder.prototype = {
|
|||
versionMinor: 0,
|
||||
versionYear: 2013,
|
||||
toolset: 'v120'
|
||||
}, cb)
|
||||
})
|
||||
},
|
||||
|
||||
// Helper - common code for VS2013 and VS2015
|
||||
findOldVS: function findOldVS (info, cb) {
|
||||
findOldVS: async function findOldVS (info) {
|
||||
const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7',
|
||||
'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7']
|
||||
const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions'
|
||||
|
||||
this.addLog(`looking for Visual Studio ${info.versionYear}`)
|
||||
this.regSearchKeys(regVC7, info.version, [], (err, res) => {
|
||||
if (err) {
|
||||
this.addLog('- not found')
|
||||
return cb(null)
|
||||
}
|
||||
|
||||
try {
|
||||
let res = await this.regSearchKeys(regVC7, info.version, [])
|
||||
const vsPath = path.resolve(res, '..')
|
||||
this.addLog(`- found in "${vsPath}"`)
|
||||
|
||||
const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32']
|
||||
this.regSearchKeys([`${regMSBuild}\\${info.version}`],
|
||||
'MSBuildToolsPath', msBuildRegOpts, (err, res) => {
|
||||
if (err) {
|
||||
this.addLog(
|
||||
'- could not find MSBuild in registry for this version')
|
||||
return cb(null)
|
||||
|
||||
try {
|
||||
res = await this.regSearchKeys([`${regMSBuild}\\${info.version}`], 'MSBuildToolsPath', msBuildRegOpts)
|
||||
} catch (err) {
|
||||
this.addLog('- could not find MSBuild in registry for this version')
|
||||
return null
|
||||
}
|
||||
|
||||
const msBuild = path.join(res, 'MSBuild.exe')
|
||||
this.addLog(`- MSBuild in "${msBuild}"`)
|
||||
|
||||
if (!this.checkConfigVersion(info.versionYear, vsPath)) {
|
||||
return cb(null)
|
||||
return null
|
||||
}
|
||||
|
||||
info.path = vsPath
|
||||
info.msBuild = msBuild
|
||||
info.sdk = null
|
||||
cb(info)
|
||||
})
|
||||
})
|
||||
return info
|
||||
} catch (err) {
|
||||
this.addLog('- not found')
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
// After finding a usable version of Visual Studio:
|
||||
|
@ -455,6 +438,8 @@ VisualStudioFinder.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
const findVisualStudio = async (nodeSemver, configMsvsVersion) => new VisualStudioFinder(nodeSemver, configMsvsVersion).findVisualStudio()
|
||||
|
||||
module.exports = findVisualStudio
|
||||
module.exports.test = {
|
||||
VisualStudioFinder,
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const { createWriteStream, promises: fs } = require('graceful-fs')
|
||||
const os = require('os')
|
||||
const { backOff } = require('exponential-backoff')
|
||||
const rm = require('rimraf')
|
||||
const tar = require('tar')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
const stream = require('stream')
|
||||
const { Transform, promises: { pipeline } } = require('stream')
|
||||
const crypto = require('crypto')
|
||||
const log = require('./log')
|
||||
const semver = require('semver')
|
||||
const fetch = require('make-fetch-happen')
|
||||
const processRelease = require('./process-release')
|
||||
const win = process.platform === 'win32'
|
||||
const streamPipeline = util.promisify(stream.pipeline)
|
||||
|
||||
/**
|
||||
* @param {typeof import('graceful-fs')} fs
|
||||
*/
|
||||
|
||||
async function install (fs, gyp, argv) {
|
||||
async function install (gyp, argv) {
|
||||
console.log()
|
||||
const release = processRelease(argv, gyp, process.version, process.release)
|
||||
// Detecting target_arch based on logic from create-cnfig-gyp.js. Used on Windows only.
|
||||
const arch = win ? (gyp.opts.target_arch || gyp.opts.arch || process.arch || 'ia32') : ''
|
||||
|
@ -60,7 +54,7 @@ async function install (fs, gyp, argv) {
|
|||
if (gyp.opts.ensure) {
|
||||
log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed')
|
||||
try {
|
||||
await fs.promises.stat(devDir)
|
||||
await fs.stat(devDir)
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
log.verbose('install', 'version not already installed, continuing with install', release.version)
|
||||
|
@ -78,7 +72,7 @@ async function install (fs, gyp, argv) {
|
|||
const installVersionFile = path.resolve(devDir, 'installVersion')
|
||||
let installVersion = 0
|
||||
try {
|
||||
const ver = await fs.promises.readFile(installVersionFile, 'ascii')
|
||||
const ver = await fs.readFile(installVersionFile, 'ascii')
|
||||
installVersion = parseInt(ver, 10) || 0
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
|
@ -100,7 +94,7 @@ async function install (fs, gyp, argv) {
|
|||
log.verbose('on Windows; need to check node.lib')
|
||||
const nodeLibPath = path.resolve(devDir, arch, 'node.lib')
|
||||
try {
|
||||
await fs.promises.stat(nodeLibPath)
|
||||
await fs.stat(nodeLibPath)
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
log.verbose('install', `version not already installed for ${arch}, continuing with install`, release.version)
|
||||
|
@ -126,12 +120,12 @@ async function install (fs, gyp, argv) {
|
|||
|
||||
async function copyDirectory (src, dest) {
|
||||
try {
|
||||
await fs.promises.stat(src)
|
||||
await fs.stat(src)
|
||||
} catch {
|
||||
throw new Error(`Missing source directory for copy: ${src}`)
|
||||
}
|
||||
await fs.promises.mkdir(dest, { recursive: true })
|
||||
const entries = await fs.promises.readdir(src, { withFileTypes: true })
|
||||
await fs.mkdir(dest, { recursive: true })
|
||||
const entries = await fs.readdir(src, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
await copyDirectory(path.join(src, entry.name), path.join(dest, entry.name))
|
||||
|
@ -140,12 +134,12 @@ async function install (fs, gyp, argv) {
|
|||
// Windows so use an exponential backoff to resolve collisions
|
||||
await backOff(async () => {
|
||||
try {
|
||||
await fs.promises.copyFile(path.join(src, entry.name), path.join(dest, entry.name))
|
||||
await fs.copyFile(path.join(src, entry.name), path.join(dest, entry.name))
|
||||
} catch (err) {
|
||||
// if ensure, check if file already exists and that's good enough
|
||||
if (gyp.opts.ensure && err.code === 'EBUSY') {
|
||||
try {
|
||||
await fs.promises.stat(path.join(dest, entry.name))
|
||||
await fs.stat(path.join(dest, entry.name))
|
||||
return
|
||||
} catch {}
|
||||
}
|
||||
|
@ -163,7 +157,7 @@ async function install (fs, gyp, argv) {
|
|||
|
||||
// first create the dir for the node dev files
|
||||
try {
|
||||
const created = await fs.promises.mkdir(devDir, { recursive: true })
|
||||
const created = await fs.mkdir(devDir, { recursive: true })
|
||||
|
||||
if (created) {
|
||||
log.verbose('created devDir', created)
|
||||
|
@ -208,7 +202,7 @@ async function install (fs, gyp, argv) {
|
|||
// on Windows there can be file errors from tar if parallel installs
|
||||
// are happening (not uncommon with multiple native modules) so
|
||||
// extract the tarball to a temp directory first and then copy over
|
||||
const tarExtractDir = win ? await fs.promises.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir
|
||||
const tarExtractDir = win ? await fs.mkdtemp(path.join(os.tmpdir(), 'node-gyp-tmp-')) : devDir
|
||||
|
||||
try {
|
||||
if (shouldDownloadTarball) {
|
||||
|
@ -228,7 +222,7 @@ async function install (fs, gyp, argv) {
|
|||
throw new Error(`${res.status} response downloading ${release.tarballUrl}`)
|
||||
}
|
||||
|
||||
await streamPipeline(
|
||||
await pipeline(
|
||||
res.body,
|
||||
// content checksum
|
||||
new ShaSum((_, checksum) => {
|
||||
|
@ -267,7 +261,7 @@ async function install (fs, gyp, argv) {
|
|||
// need to download node.lib
|
||||
...(win ? [downloadNodeLib()] : []),
|
||||
// write the "installVersion" file
|
||||
fs.promises.writeFile(installVersionPath, gyp.package.installVersion + '\n'),
|
||||
fs.writeFile(installVersionPath, gyp.package.installVersion + '\n'),
|
||||
// Only download SHASUMS.txt if we downloaded something in need of SHA verification
|
||||
...(!tarPath || win ? [downloadShasums()] : [])
|
||||
])
|
||||
|
@ -289,7 +283,7 @@ async function install (fs, gyp, argv) {
|
|||
if (tarExtractDir !== devDir) {
|
||||
try {
|
||||
// try to cleanup temp dir
|
||||
await util.promisify(rm)(tarExtractDir)
|
||||
await fs.rm(tarExtractDir, { recursive: true })
|
||||
} catch {
|
||||
log.warn('failed to clean up temp tarball extract directory')
|
||||
}
|
||||
|
@ -329,7 +323,7 @@ async function install (fs, gyp, argv) {
|
|||
log.verbose(name, 'dir', dir)
|
||||
log.verbose(name, 'url', libUrl)
|
||||
|
||||
await fs.promises.mkdir(dir, { recursive: true })
|
||||
await fs.mkdir(dir, { recursive: true })
|
||||
log.verbose('streaming', name, 'to:', targetLibPath)
|
||||
|
||||
const res = await download(gyp, libUrl)
|
||||
|
@ -339,13 +333,13 @@ async function install (fs, gyp, argv) {
|
|||
throw new Error(`${res.status} status code downloading ${name}`)
|
||||
}
|
||||
|
||||
return streamPipeline(
|
||||
return pipeline(
|
||||
res.body,
|
||||
new ShaSum((_, checksum) => {
|
||||
contentShasums[libPath] = checksum
|
||||
log.verbose('content checksum', libPath, checksum)
|
||||
}),
|
||||
fs.createWriteStream(targetLibPath)
|
||||
createWriteStream(targetLibPath)
|
||||
)
|
||||
} // downloadNodeLib()
|
||||
} // go()
|
||||
|
@ -363,7 +357,7 @@ async function install (fs, gyp, argv) {
|
|||
async function rollback (err) {
|
||||
log.warn('install', 'got an error, rolling back install')
|
||||
// roll-back the install if anything went wrong
|
||||
await util.promisify(gyp.commands.remove)([release.versionDir])
|
||||
await gyp.commands.remove([release.versionDir])
|
||||
throw err
|
||||
}
|
||||
|
||||
|
@ -394,11 +388,11 @@ async function install (fs, gyp, argv) {
|
|||
log.verbose('tmpdir == cwd', 'automatically will remove dev files after to save disk space')
|
||||
gyp.todo.push({ name: 'remove', args: argv })
|
||||
}
|
||||
return util.promisify(gyp.commands.install)([noretry].concat(argv))
|
||||
return gyp.commands.install([noretry].concat(argv))
|
||||
}
|
||||
}
|
||||
|
||||
class ShaSum extends stream.Transform {
|
||||
class ShaSum extends Transform {
|
||||
constructor (callback) {
|
||||
super()
|
||||
this._callback = callback
|
||||
|
@ -442,14 +436,12 @@ async function download (gyp, url) {
|
|||
async function readCAFile (filename) {
|
||||
// The CA file can contain multiple certificates so split on certificate
|
||||
// boundaries. [\S\s]*? is used to match everything including newlines.
|
||||
const ca = await fs.promises.readFile(filename, 'utf8')
|
||||
const ca = await fs.readFile(filename, 'utf8')
|
||||
const re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g
|
||||
return ca.match(re)
|
||||
}
|
||||
|
||||
module.exports = function (gyp, argv, callback) {
|
||||
install(fs, gyp, argv).then(callback.bind(undefined, null), callback)
|
||||
}
|
||||
module.exports = install
|
||||
module.exports.test = {
|
||||
download,
|
||||
install,
|
||||
|
|
25
lib/list.js
25
lib/list.js
|
@ -1,26 +1,25 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const fs = require('graceful-fs').promises
|
||||
const log = require('./log')
|
||||
|
||||
function list (gyp, args, callback) {
|
||||
async function list (gyp, args) {
|
||||
const devDir = gyp.devDir
|
||||
log.verbose('list', 'using node-gyp dir:', devDir)
|
||||
|
||||
fs.readdir(devDir, onreaddir)
|
||||
|
||||
function onreaddir (err, versions) {
|
||||
let versions = []
|
||||
try {
|
||||
const dir = await fs.readdir(devDir)
|
||||
if (Array.isArray(dir)) {
|
||||
versions = dir.filter((v) => v !== 'current')
|
||||
}
|
||||
} catch (err) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
return callback(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(versions)) {
|
||||
versions = versions.filter(function (v) { return v !== 'current' })
|
||||
} else {
|
||||
versions = []
|
||||
}
|
||||
callback(null, versions)
|
||||
}
|
||||
return versions
|
||||
}
|
||||
|
||||
module.exports = list
|
||||
|
|
|
@ -5,7 +5,7 @@ const nopt = require('nopt')
|
|||
const log = require('./log')
|
||||
const childProcess = require('child_process')
|
||||
const EE = require('events').EventEmitter
|
||||
const inherits = require('util').inherits
|
||||
const { inherits } = require('util')
|
||||
const commands = [
|
||||
// Module build commands
|
||||
'build',
|
||||
|
@ -27,17 +27,12 @@ function gyp () {
|
|||
}
|
||||
|
||||
function Gyp () {
|
||||
const self = this
|
||||
|
||||
this.devDir = ''
|
||||
this.commands = {}
|
||||
|
||||
commands.forEach(function (command) {
|
||||
self.commands[command] = function (argv, callback) {
|
||||
log.verbose('command', command, argv)
|
||||
return require('./' + command)(self, argv, callback)
|
||||
}
|
||||
})
|
||||
this.commands = commands.reduce((acc, command) => {
|
||||
acc[command] = (argv) => require('./' + command)(this, argv)
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
inherits(Gyp, EE)
|
||||
exports.Gyp = Gyp
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
'use strict'
|
||||
|
||||
function rebuild (gyp, argv, callback) {
|
||||
async function rebuild (gyp, argv) {
|
||||
gyp.todo.push(
|
||||
{ name: 'clean', args: [] }
|
||||
, { name: 'configure', args: argv }
|
||||
, { name: 'build', args: [] }
|
||||
)
|
||||
process.nextTick(callback)
|
||||
}
|
||||
|
||||
module.exports = rebuild
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs/promises')
|
||||
const fs = require('graceful-fs').promises
|
||||
const path = require('path')
|
||||
const log = require('./log')
|
||||
const semver = require('semver')
|
||||
|
@ -39,7 +39,5 @@ async function remove (gyp, argv) {
|
|||
await fs.rm(versionPath, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
module.exports = function (gyp, argv, callback) {
|
||||
remove(gyp, argv).then(callback.bind(undefined, null), callback)
|
||||
}
|
||||
module.exports = remove
|
||||
module.exports.usage = 'Removes the node development files for the specified version'
|
||||
|
|
47
lib/util.js
47
lib/util.js
|
@ -1,50 +1,55 @@
|
|||
'use strict'
|
||||
|
||||
const log = require('./log')
|
||||
const execFile = require('child_process').execFile
|
||||
const cp = require('child_process')
|
||||
const path = require('path')
|
||||
|
||||
function regGetValue (key, value, addOpts, cb) {
|
||||
const execFile = async (...args) => new Promise((resolve) => {
|
||||
const child = cp.execFile(...args, (...a) => resolve(a))
|
||||
child.stdin.end()
|
||||
})
|
||||
|
||||
async function regGetValue (key, value, addOpts) {
|
||||
const outReValue = value.replace(/\W/g, '.')
|
||||
const outRe = new RegExp(`^\\s+${outReValue}\\s+REG_\\w+\\s+(\\S.*)$`, 'im')
|
||||
const reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe')
|
||||
const regArgs = ['query', key, '/v', value].concat(addOpts)
|
||||
|
||||
log.silly('reg', 'running', reg, regArgs)
|
||||
const child = execFile(reg, regArgs, { encoding: 'utf8' },
|
||||
function (err, stdout, stderr) {
|
||||
const [err, stdout, stderr] = await execFile(reg, regArgs, { encoding: 'utf8' })
|
||||
|
||||
log.silly('reg', 'reg.exe stdout = %j', stdout)
|
||||
if (err || stderr.trim() !== '') {
|
||||
log.silly('reg', 'reg.exe err = %j', err && (err.stack || err))
|
||||
log.silly('reg', 'reg.exe stderr = %j', stderr)
|
||||
return cb(err, stderr)
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
throw new Error(stderr)
|
||||
}
|
||||
|
||||
const result = outRe.exec(stdout)
|
||||
if (!result) {
|
||||
log.silly('reg', 'error parsing stdout')
|
||||
return cb(new Error('Could not parse output of reg.exe'))
|
||||
}
|
||||
log.silly('reg', 'found: %j', result[1])
|
||||
cb(null, result[1])
|
||||
})
|
||||
child.stdin.end()
|
||||
throw new Error('Could not parse output of reg.exe')
|
||||
}
|
||||
|
||||
function regSearchKeys (keys, value, addOpts, cb) {
|
||||
let i = 0
|
||||
const search = () => {
|
||||
log.silly('reg-search', 'looking for %j in %j', value, keys[i])
|
||||
regGetValue(keys[i], value, addOpts, (err, res) => {
|
||||
++i
|
||||
if (err && i < keys.length) { return search() }
|
||||
cb(err, res)
|
||||
})
|
||||
log.silly('reg', 'found: %j', result[1])
|
||||
return result[1]
|
||||
}
|
||||
|
||||
async function regSearchKeys (keys, value, addOpts) {
|
||||
for (const key of keys) {
|
||||
try {
|
||||
return await regGetValue(key, value, addOpts)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
search()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
execFile,
|
||||
regGetValue,
|
||||
regSearchKeys
|
||||
}
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const childProcess = require('child_process')
|
||||
|
||||
function startsWith (str, search, pos) {
|
||||
if (String.prototype.startsWith) {
|
||||
return str.startsWith(search, pos)
|
||||
}
|
||||
|
||||
return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search
|
||||
}
|
||||
|
||||
function processExecSync (file, args, options) {
|
||||
let error, command
|
||||
command = makeCommand(file, args)
|
||||
|
||||
/*
|
||||
this function emulates child_process.execSync for legacy node <= 0.10.x
|
||||
derived from https://github.com/gvarsanyi/sync-exec/blob/master/js/sync-exec.js
|
||||
*/
|
||||
|
||||
options = options || {}
|
||||
// init timeout
|
||||
const timeout = Date.now() + options.timeout
|
||||
// init tmpdir
|
||||
let osTempBase = '/tmp'
|
||||
const os = determineOS()
|
||||
osTempBase = '/tmp'
|
||||
|
||||
if (process.env.TMP) {
|
||||
osTempBase = process.env.TMP
|
||||
}
|
||||
|
||||
if (osTempBase[osTempBase.length - 1] !== '/') {
|
||||
osTempBase += '/'
|
||||
}
|
||||
|
||||
const tmpdir = osTempBase + 'processExecSync.' + Date.now() + Math.random()
|
||||
fs.mkdirSync(tmpdir)
|
||||
|
||||
// init command
|
||||
if (os === 'linux') {
|
||||
command = '(' + command + ' > ' + tmpdir + '/stdout 2> ' + tmpdir +
|
||||
'/stderr); echo $? > ' + tmpdir + '/status'
|
||||
} else {
|
||||
command = '(' + command + ' > ' + tmpdir + '/stdout 2> ' + tmpdir +
|
||||
'/stderr) | echo %errorlevel% > ' + tmpdir + '/status | exit'
|
||||
}
|
||||
|
||||
// init child
|
||||
const child = childProcess.exec(command, options)
|
||||
|
||||
const maxTry = 100000 // increases the test time by 6 seconds on win-2016-node-0.10
|
||||
let tryCount = 0
|
||||
while (tryCount < maxTry) {
|
||||
try {
|
||||
const x = fs.readFileSync(tmpdir + '/status')
|
||||
if (x.toString() === '0') {
|
||||
break
|
||||
}
|
||||
} catch (ignore) {}
|
||||
tryCount++
|
||||
if (Date.now() > timeout) {
|
||||
error = child
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
['stdout', 'stderr', 'status'].forEach(function (file) {
|
||||
child[file] = fs.readFileSync(tmpdir + '/' + file, options.encoding)
|
||||
setTimeout(unlinkFile, 500, tmpdir + '/' + file)
|
||||
})
|
||||
|
||||
child.status = Number(child.status)
|
||||
if (child.status !== 0) {
|
||||
error = child
|
||||
}
|
||||
|
||||
try {
|
||||
fs.rmdirSync(tmpdir)
|
||||
} catch (ignore) {}
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
return child.stdout
|
||||
}
|
||||
|
||||
function makeCommand (file, args) {
|
||||
let command, quote
|
||||
command = file
|
||||
if (args.length > 0) {
|
||||
for (const i in args) {
|
||||
command = command + ' '
|
||||
if (args[i][0] === '-') {
|
||||
command = command + args[i]
|
||||
} else {
|
||||
if (!quote) {
|
||||
command = command + '"'
|
||||
quote = true
|
||||
}
|
||||
command = command + args[i]
|
||||
if (quote) {
|
||||
if (args.length === (parseInt(i) + 1)) {
|
||||
command = command + '"'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
function determineOS () {
|
||||
let os = ''
|
||||
let tmpVar = ''
|
||||
if (process.env.OSTYPE) {
|
||||
tmpVar = process.env.OSTYPE
|
||||
} else if (process.env.OS) {
|
||||
tmpVar = process.env.OS
|
||||
} else {
|
||||
// default is linux
|
||||
tmpVar = 'linux'
|
||||
}
|
||||
|
||||
if (startsWith(tmpVar, 'linux')) {
|
||||
os = 'linux'
|
||||
}
|
||||
if (startsWith(tmpVar, 'win')) {
|
||||
os = 'win'
|
||||
}
|
||||
|
||||
return os
|
||||
}
|
||||
|
||||
function unlinkFile (file) {
|
||||
fs.unlinkSync(file)
|
||||
}
|
||||
|
||||
module.exports = processExecSync
|
|
@ -4,12 +4,10 @@ const { describe, it } = require('mocha')
|
|||
const assert = require('assert')
|
||||
const path = require('path')
|
||||
const fs = require('graceful-fs')
|
||||
const childProcess = require('child_process')
|
||||
const { execFileSync, execFile } = require('child_process')
|
||||
const os = require('os')
|
||||
const addonPath = path.resolve(__dirname, 'node_modules', 'hello_world')
|
||||
const nodeGyp = path.resolve(__dirname, '..', 'bin', 'node-gyp.js')
|
||||
const execFileSync = childProcess.execFileSync || require('./process-exec-sync')
|
||||
const execFile = childProcess.execFile
|
||||
|
||||
function runHello (hostProcess) {
|
||||
if (!hostProcess) {
|
||||
|
|
|
@ -9,13 +9,12 @@ const log = require('../lib/log')
|
|||
const requireInject = require('require-inject')
|
||||
const configure = requireInject('../lib/configure', {
|
||||
'graceful-fs': {
|
||||
openSync: function () { return 0 },
|
||||
closeSync: function () { },
|
||||
writeFile: function (file, data, cb) { cb() },
|
||||
stat: function (file, cb) { cb(null, {}) },
|
||||
mkdir: function (dir, options, cb) { cb() },
|
||||
openSync: () => 0,
|
||||
closeSync: () => {},
|
||||
promises: {
|
||||
writeFile: function (file, data) { return Promise.resolve(null) }
|
||||
stat: async () => ({}),
|
||||
mkdir: async () => {},
|
||||
writeFile: async () => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,7 +4,6 @@ const { describe, it, after } = require('mocha')
|
|||
const assert = require('assert')
|
||||
const fs = require('fs/promises')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const install = require('../lib/install')
|
||||
|
@ -176,7 +175,7 @@ describe('download', function () {
|
|||
prog.parseArgv([])
|
||||
prog.devDir = devDir
|
||||
log.level = 'warn'
|
||||
await util.promisify(install)(prog, [])
|
||||
await install(prog, [])
|
||||
|
||||
const data = await fs.readFile(path.join(expectedDir, 'installVersion'), 'utf8')
|
||||
assert.strictEqual(data, '11\n', 'correct installVersion')
|
||||
|
|
|
@ -4,23 +4,17 @@ delete process.env.PYTHON
|
|||
|
||||
const { describe, it } = require('mocha')
|
||||
const assert = require('assert')
|
||||
const findPython = require('../lib/find-python')
|
||||
const execFile = require('child_process').execFile
|
||||
const PythonFinder = findPython.test.PythonFinder
|
||||
const { test: { PythonFinder, findPython: testFindPython } } = require('../lib/find-python')
|
||||
const { execFile } = require('../lib/util')
|
||||
|
||||
describe('find-python', function () {
|
||||
it('find python', function () {
|
||||
findPython.test.findPython(null, function (err, found) {
|
||||
assert.strictEqual(err, null)
|
||||
const proc = execFile(found, ['-V'], function (err, stdout, stderr) {
|
||||
it('find python', async function () {
|
||||
const found = await testFindPython(null)
|
||||
const [err, stdout, stderr] = await execFile(found, ['-V'], { encoding: 'utf-8' })
|
||||
assert.strictEqual(err, null)
|
||||
assert.ok(/Python 3/.test(stdout))
|
||||
assert.strictEqual(stderr, '')
|
||||
})
|
||||
proc.stdout.setEncoding('utf-8')
|
||||
proc.stderr.setEncoding('utf-8')
|
||||
})
|
||||
})
|
||||
|
||||
function poison (object, property) {
|
||||
function fail () {
|
||||
|
@ -36,109 +30,105 @@ describe('find-python', function () {
|
|||
Object.defineProperty(object, property, descriptor)
|
||||
}
|
||||
|
||||
function TestPythonFinder () {
|
||||
PythonFinder.apply(this, arguments)
|
||||
}
|
||||
function TestPythonFinder () { PythonFinder.apply(this, arguments) }
|
||||
TestPythonFinder.prototype = Object.create(PythonFinder.prototype)
|
||||
delete TestPythonFinder.prototype.env.NODE_GYP_FORCE_PYTHON
|
||||
const findPython = async (f) => {
|
||||
try {
|
||||
return { err: null, python: await f.findPython() }
|
||||
} catch (err) {
|
||||
return { err, python: null }
|
||||
}
|
||||
}
|
||||
|
||||
it('find python - python', function () {
|
||||
const f = new TestPythonFinder('python', done)
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
it('find python - python', async function () {
|
||||
const f = new TestPythonFinder('python')
|
||||
f.execFile = async function (program, args, opts) {
|
||||
f.execFile = async function (program, args, opts) {
|
||||
poison(f, 'execFile')
|
||||
assert.strictEqual(program, '/path/python')
|
||||
assert.ok(/sys\.version_info/.test(args[1]))
|
||||
cb(null, '3.9.1')
|
||||
return [null, '3.9.1']
|
||||
}
|
||||
assert.strictEqual(program,
|
||||
process.platform === 'win32' ? '"python"' : 'python')
|
||||
assert.strictEqual(program, process.platform === 'win32' ? '"python"' : 'python')
|
||||
assert.ok(/sys\.executable/.test(args[1]))
|
||||
cb(null, '/path/python')
|
||||
return [null, '/path/python']
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err, python) {
|
||||
const { err, python } = await findPython(f)
|
||||
assert.strictEqual(err, null)
|
||||
assert.strictEqual(python, '/path/python')
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - python too old', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
it('find python - python too old', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(null, '/path/python')
|
||||
return [null, '/path/python']
|
||||
} else if (/sys\.version_info/.test(args[args.length - 1])) {
|
||||
cb(null, '2.3.4')
|
||||
return [null, '2.3.4']
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err) {
|
||||
const { err } = await findPython(f)
|
||||
assert.ok(/Could not find any Python/.test(err))
|
||||
assert.ok(/not supported/i.test(f.errorLog))
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - no python', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
it('find python - no python', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else if (/sys\.version_info/.test(args[args.length - 1])) {
|
||||
cb(new Error('not a Python executable'))
|
||||
throw new Error('not a Python executable')
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err) {
|
||||
const { err } = await findPython(f)
|
||||
assert.ok(/Could not find any Python/.test(err))
|
||||
assert.ok(/not in PATH/.test(f.errorLog))
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - no python2, no python, unix', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
it('find python - no python2, no python, unix', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.checkPyLauncher = assert.fail
|
||||
f.win = false
|
||||
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err) {
|
||||
const { err } = await findPython(f)
|
||||
assert.ok(/Could not find any Python/.test(err))
|
||||
assert.ok(/not in PATH/.test(f.errorLog))
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - no python, use python launcher', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
it('find python - no python, use python launcher', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.win = true
|
||||
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (program === 'py.exe') {
|
||||
assert.notStrictEqual(args.indexOf('-3'), -1)
|
||||
assert.notStrictEqual(args.indexOf('-c'), -1)
|
||||
return cb(null, 'Z:\\snake.exe')
|
||||
return [null, 'Z:\\snake.exe']
|
||||
}
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else if (f.winDefaultLocations.includes(program)) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else if (/sys\.version_info/.test(args[args.length - 1])) {
|
||||
if (program === 'Z:\\snake.exe') {
|
||||
cb(null, '3.9.0')
|
||||
return [null, '3.9.0']
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
|
@ -146,58 +136,49 @@ describe('find-python', function () {
|
|||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err, python) {
|
||||
const { err, python } = await findPython(f)
|
||||
assert.strictEqual(err, null)
|
||||
assert.strictEqual(python, 'Z:\\snake.exe')
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - no python, no python launcher, good guess', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
it('find python - no python, no python launcher, good guess', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.win = true
|
||||
const expectedProgram = f.winDefaultLocations[0]
|
||||
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (program === 'py.exe') {
|
||||
return cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
}
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else if (program === expectedProgram &&
|
||||
/sys\.version_info/.test(args[args.length - 1])) {
|
||||
cb(null, '3.7.3')
|
||||
return [null, '3.7.3']
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err, python) {
|
||||
const { err, python } = await findPython(f)
|
||||
assert.strictEqual(err, null)
|
||||
assert.ok(python === expectedProgram)
|
||||
}
|
||||
})
|
||||
|
||||
it('find python - no python, no python launcher, bad guess', function () {
|
||||
const f = new TestPythonFinder(null, done)
|
||||
it('find python - no python, no python launcher, bad guess', async function () {
|
||||
const f = new TestPythonFinder(null)
|
||||
f.win = true
|
||||
|
||||
f.execFile = function (program, args, opts, cb) {
|
||||
f.execFile = async function (program, args, opts) {
|
||||
if (/sys\.executable/.test(args[args.length - 1])) {
|
||||
cb(new Error('not found'))
|
||||
throw new Error('not found')
|
||||
} else if (/sys\.version_info/.test(args[args.length - 1])) {
|
||||
cb(new Error('not a Python executable'))
|
||||
throw new Error('not a Python executable')
|
||||
} else {
|
||||
assert.fail()
|
||||
}
|
||||
}
|
||||
f.findPython()
|
||||
|
||||
function done (err) {
|
||||
const { err } = await findPython(f)
|
||||
assert.ok(/Could not find any Python/.test(err))
|
||||
assert.ok(/not in PATH/.test(f.errorLog))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,8 +4,7 @@ const { describe, it } = require('mocha')
|
|||
const assert = require('assert')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const findVisualStudio = require('../lib/find-visualstudio')
|
||||
const VisualStudioFinder = findVisualStudio.test.VisualStudioFinder
|
||||
const { test: { VisualStudioFinder } } = require('../lib/find-visualstudio')
|
||||
|
||||
const semverV1 = { major: 1, minor: 0, patch: 0 }
|
||||
|
||||
|
@ -28,9 +27,42 @@ function poison (object, property) {
|
|||
function TestVisualStudioFinder () { VisualStudioFinder.apply(this, arguments) }
|
||||
TestVisualStudioFinder.prototype = Object.create(VisualStudioFinder.prototype)
|
||||
|
||||
const findVisualStudio = async (finder) => {
|
||||
try {
|
||||
return { err: null, info: await finder.findVisualStudio() }
|
||||
} catch (err) {
|
||||
return { err, info: null }
|
||||
}
|
||||
}
|
||||
|
||||
describe('find-visualstudio', function () {
|
||||
it('VS2013', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
it('VS2013', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
return finder.parseData(new Error(), '', '')
|
||||
}
|
||||
finder.regSearchKeys = async (keys, value, addOpts) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
continue
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return 'C:\\VS2013\\VC\\'
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\12.0\\MSBuildToolsPath':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return 'C:\\MSBuild12\\'
|
||||
default:
|
||||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
throw new Error()
|
||||
}
|
||||
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\MSBuild12\\MSBuild.exe',
|
||||
|
@ -44,47 +76,19 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
finder.parseData(new Error(), '', '', cb)
|
||||
}
|
||||
finder.regSearchKeys = (keys, value, addOpts, cb) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
continue
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return cb(null, 'C:\\VS2013\\VC\\')
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\12.0\\MSBuildToolsPath':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return cb(null, 'C:\\MSBuild12\\')
|
||||
default:
|
||||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
return cb(new Error())
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('VS2013 should not be found on new node versions', function () {
|
||||
it('VS2013 should not be found on new node versions', async function () {
|
||||
const finder = new TestVisualStudioFinder({
|
||||
major: 10,
|
||||
minor: 0,
|
||||
patch: 0
|
||||
}, null, (err, info) => {
|
||||
assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
|
||||
assert.ok(!info, 'no data')
|
||||
})
|
||||
}, null)
|
||||
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
finder.regSearchKeys = (keys, value, addOpts, cb) => {
|
||||
finder.regSearchKeys = async (keys, value, addOpts) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
|
@ -95,13 +99,37 @@ describe('find-visualstudio', function () {
|
|||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
return cb(new Error())
|
||||
throw new Error()
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
|
||||
assert.ok(!info, 'no data')
|
||||
})
|
||||
|
||||
it('VS2015', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
it('VS2015', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
return finder.parseData(new Error(), '', '')
|
||||
}
|
||||
finder.regSearchKeys = async (keys, value, addOpts) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return 'C:\\VS2015\\VC\\'
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\14.0\\MSBuildToolsPath':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return 'C:\\MSBuild14\\'
|
||||
default:
|
||||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
throw new Error()
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\MSBuild14\\MSBuild.exe',
|
||||
|
@ -115,29 +143,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
finder.parseData(new Error(), '', '', cb)
|
||||
}
|
||||
finder.regSearchKeys = (keys, value, addOpts, cb) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return cb(null, 'C:\\VS2015\\VC\\')
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\14.0\\MSBuildToolsPath':
|
||||
assert.ok(true, `expected search for registry value ${fullName}`)
|
||||
return cb(null, 'C:\\MSBuild14\\')
|
||||
default:
|
||||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
return cb(new Error())
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('error from PowerShell', function () {
|
||||
it('error from PowerShell', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(new Error(), '', '', (info) => {
|
||||
|
@ -146,7 +152,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('empty output from PowerShell', function () {
|
||||
it('empty output from PowerShell', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(null, '', '', (info) => {
|
||||
|
@ -155,7 +161,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('output from PowerShell not JSON', function () {
|
||||
it('output from PowerShell not JSON', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(null, 'AAAABBBB', '', (info) => {
|
||||
|
@ -164,7 +170,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('wrong JSON from PowerShell', function () {
|
||||
it('wrong JSON from PowerShell', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(null, '{}', '', (info) => {
|
||||
|
@ -173,7 +179,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('empty JSON from PowerShell', function () {
|
||||
it('empty JSON from PowerShell', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(null, '[]', '', (info) => {
|
||||
|
@ -182,7 +188,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('future version', function () {
|
||||
it('future version', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
finder.parseData(null, JSON.stringify([{
|
||||
|
@ -200,7 +206,7 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('single unusable VS2017', function () {
|
||||
it('single unusable VS2017', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, null)
|
||||
|
||||
const file = path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt')
|
||||
|
@ -212,8 +218,17 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('minimal VS2017 Build Tools', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
it('minimal VS2017 Build Tools', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2017_BuildTools_minimal.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
|
||||
|
@ -229,18 +244,17 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2017_BuildTools_minimal.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('VS2017 Community with C++ workload', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
it('VS2017 Community with C++ workload', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2017_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
|
||||
|
@ -256,18 +270,16 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2017_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('VS2017 Express', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
it('VS2017 Express', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures', 'VS_2017_Express.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
|
||||
|
@ -283,17 +295,17 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures', 'VS_2017_Express.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('VS2019 Preview with C++ workload', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
it('VS2019 Preview with C++ workload', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_Preview.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
|
||||
|
@ -309,18 +321,17 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_Preview.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('minimal VS2019 Build Tools', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
it('minimal VS2019 Build Tools', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_BuildTools_minimal.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
|
||||
|
@ -336,18 +347,17 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_BuildTools_minimal.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('VS2019 Community with C++ workload', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
it('VS2019 Community with C++ workload', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
|
||||
|
@ -363,24 +373,26 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2019_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('VS2022 Preview with C++ workload', function () {
|
||||
it('VS2022 Preview with C++ workload', async function () {
|
||||
const msBuildPath = process.arch === 'arm64'
|
||||
? 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' +
|
||||
'Community\\MSBuild\\Current\\Bin\\arm64\\MSBuild.exe'
|
||||
: 'C:\\Program Files\\Microsoft Visual Studio\\2022\\' +
|
||||
'Community\\MSBuild\\Current\\Bin\\MSBuild.exe'
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.msBuildPathExists = (path) => {
|
||||
return true
|
||||
}
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2022_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info, {
|
||||
msBuild: msBuildPath,
|
||||
|
@ -395,21 +407,8 @@ describe('find-visualstudio', function () {
|
|||
})
|
||||
})
|
||||
|
||||
poison(finder, 'regSearchKeys')
|
||||
finder.msBuildPathExists = (path) => {
|
||||
return true
|
||||
}
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
const file = path.join(__dirname, 'fixtures',
|
||||
'VS_2022_Community_workload.txt')
|
||||
const data = fs.readFileSync(file)
|
||||
finder.parseData(null, data, '', cb)
|
||||
}
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
function allVsVersions (finder) {
|
||||
finder.findVisualStudio2017OrNewer = (cb) => {
|
||||
finder.findVisualStudio2017OrNewer = async () => {
|
||||
const data0 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures',
|
||||
'VS_2017_Unusable.txt')))
|
||||
const data1 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures',
|
||||
|
@ -428,9 +427,9 @@ describe('find-visualstudio', function () {
|
|||
'VS_2022_Community_workload.txt')))
|
||||
const data = JSON.stringify(data0.concat(data1, data2, data3, data4,
|
||||
data5, data6, data7))
|
||||
finder.parseData(null, data, '', cb)
|
||||
return finder.parseData(null, data, '')
|
||||
}
|
||||
finder.regSearchKeys = (keys, value, addOpts, cb) => {
|
||||
finder.regSearchKeys = async (keys, value, addOpts) => {
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const fullName = `${keys[i]}\\${value}`
|
||||
switch (fullName) {
|
||||
|
@ -438,225 +437,201 @@ describe('find-visualstudio', function () {
|
|||
case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0':
|
||||
continue
|
||||
case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0':
|
||||
return cb(null, 'C:\\VS2013\\VC\\')
|
||||
return 'C:\\VS2013\\VC\\'
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\12.0\\MSBuildToolsPath':
|
||||
return cb(null, 'C:\\MSBuild12\\')
|
||||
return 'C:\\MSBuild12\\'
|
||||
case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0':
|
||||
return cb(null, 'C:\\VS2015\\VC\\')
|
||||
return 'C:\\VS2015\\VC\\'
|
||||
case 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions\\14.0\\MSBuildToolsPath':
|
||||
return cb(null, 'C:\\MSBuild14\\')
|
||||
return 'C:\\MSBuild14\\'
|
||||
default:
|
||||
assert.fail(`unexpected search for registry value ${fullName}`)
|
||||
}
|
||||
}
|
||||
return cb(new Error())
|
||||
throw new Error()
|
||||
}
|
||||
}
|
||||
|
||||
it('fail when looking for invalid path', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'AABB', (err, info) => {
|
||||
it('fail when looking for invalid path', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'AABB')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
|
||||
assert.ok(!info, 'no data')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2013 by version number', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2013')
|
||||
|
||||
it('look for VS2013 by version number', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2013', (err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2013)
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2013 by installation path', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013')
|
||||
|
||||
it('look for VS2013 by installation path', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013',
|
||||
(err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path, 'C:\\VS2013')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2015 by version number', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2015')
|
||||
|
||||
it('look for VS2015 by version number', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2015', (err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2015)
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2015 by installation path', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
|
||||
|
||||
it('look for VS2015 by installation path', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015',
|
||||
(err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path, 'C:\\VS2015')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2017 by version number', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2017')
|
||||
|
||||
it('look for VS2017 by version number', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2017', (err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2017)
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('look for VS2017 by installation path', function () {
|
||||
it('look for VS2017 by installation path', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community',
|
||||
(err, info) => {
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
it('look for VS2019 by version number', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2019')
|
||||
|
||||
it('look for VS2019 by version number', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2019', (err, info) => {
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2019)
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('look for VS2019 by installation path', function () {
|
||||
it('look for VS2019 by installation path', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools',
|
||||
(err, info) => {
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('look for VS2022 by version number', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2022', (err, info) => {
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2022)
|
||||
})
|
||||
it('look for VS2022 by version number', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, '2022')
|
||||
|
||||
finder.msBuildPathExists = (path) => {
|
||||
return true
|
||||
}
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2022)
|
||||
})
|
||||
|
||||
it('msvs_version match should be case insensitive', function () {
|
||||
it('msvs_version match should be case insensitive', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1,
|
||||
'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS',
|
||||
(err, info) => {
|
||||
'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('latest version should be found by default', function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2022)
|
||||
})
|
||||
it('latest version should be found by default', async function () {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
finder.msBuildPathExists = (path) => {
|
||||
return true
|
||||
}
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.versionYear, 2022)
|
||||
})
|
||||
|
||||
it('run on a usable VS Command Prompt', function () {
|
||||
it('run on a usable VS Command Prompt', async function () {
|
||||
process.env.VCINSTALLDIR = 'C:\\VS2015\\VC'
|
||||
// VSINSTALLDIR is not defined on Visual C++ Build Tools 2015
|
||||
delete process.env.VSINSTALLDIR
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path, 'C:\\VS2015')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('VCINSTALLDIR match should be case insensitive', function () {
|
||||
it('VCINSTALLDIR match should be case insensitive', async function () {
|
||||
process.env.VCINSTALLDIR =
|
||||
'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS\\VC'
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path,
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('run on a unusable VS Command Prompt', function () {
|
||||
it('run on a unusable VS Command Prompt', async function () {
|
||||
process.env.VCINSTALLDIR =
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildToolsUnusable\\VC'
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, null)
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
|
||||
assert.ok(!info, 'no data')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('run on a VS Command Prompt with matching msvs_version', function () {
|
||||
it('run on a VS Command Prompt with matching msvs_version', async function () {
|
||||
process.env.VCINSTALLDIR = 'C:\\VS2015\\VC'
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015',
|
||||
(err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.strictEqual(err, null)
|
||||
assert.deepStrictEqual(info.path, 'C:\\VS2015')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
|
||||
it('run on a VS Command Prompt with mismatched msvs_version', function () {
|
||||
it('run on a VS Command Prompt with mismatched msvs_version', async function () {
|
||||
process.env.VCINSTALLDIR =
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC'
|
||||
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015',
|
||||
(err, info) => {
|
||||
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
|
||||
|
||||
allVsVersions(finder)
|
||||
const { err, info } = await findVisualStudio(finder)
|
||||
assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
|
||||
assert.ok(!info, 'no data')
|
||||
})
|
||||
|
||||
allVsVersions(finder)
|
||||
finder.findVisualStudio()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,53 +1,57 @@
|
|||
'use strict'
|
||||
|
||||
const { describe, it, after } = require('mocha')
|
||||
const { rm } = require('fs/promises')
|
||||
const { rm, mkdtemp } = require('fs/promises')
|
||||
const { createWriteStream } = require('fs')
|
||||
const assert = require('assert')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
const util = require('util')
|
||||
const { test: { download, install } } = require('../lib/install')
|
||||
const gyp = require('../lib/node-gyp')
|
||||
const semver = require('semver')
|
||||
const stream = require('stream')
|
||||
const streamPipeline = util.promisify(stream.pipeline)
|
||||
const { pipeline: streamPipeline } = require('stream/promises')
|
||||
const requireInject = require('require-inject')
|
||||
const gyp = require('../lib/node-gyp')
|
||||
|
||||
const createInstall = (mocks = {}) => requireInject('../lib/install', mocks).test
|
||||
const { download, install } = createInstall()
|
||||
|
||||
describe('install', function () {
|
||||
it('EACCES retry once', async () => {
|
||||
const fs = {
|
||||
let statCalled = 0
|
||||
const mockInstall = createInstall({
|
||||
'graceful-fs': {
|
||||
promises: {
|
||||
stat (_) {
|
||||
const err = new Error()
|
||||
err.code = 'EACCES'
|
||||
assert.ok(true)
|
||||
statCalled++
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
const Gyp = {
|
||||
devDir: __dirname,
|
||||
opts: {
|
||||
ensure: true
|
||||
},
|
||||
commands: {
|
||||
install (argv, cb) {
|
||||
install(fs, Gyp, argv).then(cb, cb)
|
||||
},
|
||||
remove (_, cb) {
|
||||
cb()
|
||||
}
|
||||
install: (...args) => mockInstall.install(Gyp, ...args),
|
||||
remove: async () => {}
|
||||
}
|
||||
}
|
||||
|
||||
let err
|
||||
try {
|
||||
await install(fs, Gyp, [])
|
||||
} catch (err) {
|
||||
assert.ok(true)
|
||||
await Gyp.commands.install([])
|
||||
} catch (e) {
|
||||
err = e
|
||||
}
|
||||
|
||||
assert.ok(err)
|
||||
assert.equal(statCalled, 2)
|
||||
if (/"pre" versions of node cannot be installed/.test(err.message)) {
|
||||
assert.ok(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// only run these tests if we are running a version of Node with predictable version path behavior
|
||||
|
@ -56,7 +60,7 @@ describe('install', function () {
|
|||
semver.prerelease(process.version) !== null ||
|
||||
semver.satisfies(process.version, '<10')
|
||||
|
||||
async function parallelInstallsTest (test, fs, devDir, prog) {
|
||||
async function parallelInstallsTest (test, devDir, prog) {
|
||||
if (skipParallelInstallTests) {
|
||||
return test.skip('Skipping parallel installs test due to test environment configuration')
|
||||
}
|
||||
|
@ -69,52 +73,49 @@ describe('install', function () {
|
|||
await rm(expectedDir, { recursive: true, force: true })
|
||||
|
||||
await Promise.all([
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, []),
|
||||
install(fs, prog, [])
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, []),
|
||||
install(prog, [])
|
||||
])
|
||||
}
|
||||
|
||||
it('parallel installs (ensure=true)', async function () {
|
||||
this.timeout(600000)
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const devDir = await util.promisify(fs.mkdtemp)(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
const devDir = await mkdtemp(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
|
||||
const prog = gyp()
|
||||
prog.parseArgv([])
|
||||
prog.devDir = devDir
|
||||
prog.opts.ensure = true
|
||||
|
||||
await parallelInstallsTest(this, fs, devDir, prog)
|
||||
await parallelInstallsTest(this, devDir, prog)
|
||||
})
|
||||
|
||||
it('parallel installs (ensure=false)', async function () {
|
||||
this.timeout(600000)
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const devDir = await util.promisify(fs.mkdtemp)(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
const devDir = await mkdtemp(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
|
||||
const prog = gyp()
|
||||
prog.parseArgv([])
|
||||
prog.devDir = devDir
|
||||
prog.opts.ensure = false
|
||||
|
||||
await parallelInstallsTest(this, fs, devDir, prog)
|
||||
await parallelInstallsTest(this, devDir, prog)
|
||||
})
|
||||
|
||||
it('parallel installs (tarball)', async function () {
|
||||
this.timeout(600000)
|
||||
|
||||
const fs = require('graceful-fs')
|
||||
const devDir = await util.promisify(fs.mkdtemp)(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
const devDir = await mkdtemp(path.join(os.tmpdir(), 'node-gyp-test-'))
|
||||
|
||||
const prog = gyp()
|
||||
prog.parseArgv([])
|
||||
|
@ -123,9 +124,9 @@ describe('install', function () {
|
|||
|
||||
await streamPipeline(
|
||||
(await download(prog, `https://nodejs.org/dist/${process.version}/node-${process.version}.tar.gz`)).body,
|
||||
fs.createWriteStream(prog.opts.tarball)
|
||||
createWriteStream(prog.opts.tarball)
|
||||
)
|
||||
|
||||
await parallelInstallsTest(this, fs, devDir, prog)
|
||||
await parallelInstallsTest(this, devDir, prog)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue