diff --git a/bin/node-gyp.js b/bin/node-gyp.js index 3441973da4..a6f3e73a50 100755 --- a/bin/node-gyp.js +++ b/bin/node-gyp.js @@ -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) { - log.error(command.name + ' error') - log.error('stack', err.stack) - errorMessage() - log.error('not ok') - return process.exit(1) - } + try { + const args = await prog.commands[command.name](command.args) ?? [] + if (command.name === 'list') { - const versions = arguments[1] - if (versions.length > 0) { - versions.forEach(function (version) { - console.log(version) - }) + 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 (arguments.length >= 2) { - console.log.apply(console, [].slice.call(arguments, 1)) + } else if (args.length >= 1) { + console.log(...args.slice(1)) } // now run the next command in the queue - process.nextTick(run) - }) + return run() + } catch (err) { + log.error(command.name + ' error') + log.error('stack', err.stack) + errorMessage() + log.error('not ok') + return process.exit(1) + } } process.on('exit', function (code) { diff --git a/lib/build.js b/lib/build.js index 0bcc9bea19..c5579553f7 100644 --- a/lib/build.js +++ b/lib/build.js @@ -203,13 +203,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 }) - } + await fs.rm(buildBinsDir, { recursive: true }) } if (code !== 0) { @@ -223,7 +217,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' diff --git a/lib/clean.js b/lib/clean.js index 844b47aed1..523f8016ca 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -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' diff --git a/lib/configure.js b/lib/configure.js index 01c3f497ad..8ccfc3fad5 100644 --- a/lib/configure.js +++ b/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) - } - log.verbose('get node dir', 'target node version installed:', release.versionDir) - nodeDir = path.resolve(gyp.devDir, release.versionDir) - createBuildDir() - }) + await gyp.commands.install([release.version]) + + log.verbose('get node dir', 'target node version installed:', release.versionDir) + nodeDir = path.resolve(gyp.devDir, release.versionDir) } + + return createBuildDir() } - function 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) - } - 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 isNew = await fs.mkdir(buildDir, { recursive: true }) + log.verbose( + 'build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No' + ) + 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 => { - configs.push(configPath) - findConfigs() - }).catch(err => { - callback(err) - }) + const configPath = await createConfigGypi({ gyp, buildDir, nodeDir, vsInfo, python }) + configs.push(configPath) + 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 { - log.verbose(name, 'found gypi file') - configs.push(fullPath) - findConfigs() + try { + await fs.stat(fullPath) + log.verbose(name, 'found gypi file') + configs.push(fullPath) + } 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,79 +202,79 @@ 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) { - commonGypi = path.resolve(nodeDir, 'common.gypi') - } + try { + await fs.stat(commonGypi) + } catch (err) { + commonGypi = path.resolve(nodeDir, 'common.gypi') + } - let outputDir = 'build' - if (win) { - // Windows expects an absolute path - outputDir = buildDir - } - const nodeGypDir = path.resolve(__dirname, '..') - - let nodeLibFile = path.join(nodeDir, - !gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)', - release.name + '.lib') - - argv.push('-I', addonGypi) - argv.push('-I', commonGypi) - argv.push('-Dlibrary=shared_library') - argv.push('-Dvisibility=default') - argv.push('-Dnode_root_dir=' + nodeDir) - if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') { - argv.push('-Dnode_exp_file=' + nodeExpFile) - if (process.platform === 'os390' && zoslibIncDir) { - argv.push('-Dzoslib_include_dir=' + zoslibIncDir) - } - } - argv.push('-Dnode_gyp_dir=' + nodeGypDir) + let outputDir = 'build' + if (win) { + // Windows expects an absolute path + outputDir = buildDir + } + const nodeGypDir = path.resolve(__dirname, '..') - // Do this to keep Cygwin environments happy, else the unescaped '\' gets eaten up, - // resulting in bad paths, Ex c:parentFolderfolderanotherFolder instead of c:\parentFolder\folder\anotherFolder - if (win) { - nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\') + let nodeLibFile = path.join(nodeDir, + !gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)', + release.name + '.lib') + + argv.push('-I', addonGypi) + argv.push('-I', commonGypi) + argv.push('-Dlibrary=shared_library') + argv.push('-Dvisibility=default') + argv.push('-Dnode_root_dir=' + nodeDir) + if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') { + argv.push('-Dnode_exp_file=' + nodeExpFile) + if (process.platform === 'os390' && zoslibIncDir) { + argv.push('-Dzoslib_include_dir=' + zoslibIncDir) } - argv.push('-Dnode_lib_file=' + nodeLibFile) - argv.push('-Dmodule_root_dir=' + process.cwd()) - argv.push('-Dnode_engine=' + + } + argv.push('-Dnode_gyp_dir=' + nodeGypDir) + + // Do this to keep Cygwin environments happy, else the unescaped '\' gets eaten up, + // resulting in bad paths, Ex c:parentFolderfolderanotherFolder instead of c:\parentFolder\folder\anotherFolder + if (win) { + nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\') + } + argv.push('-Dnode_lib_file=' + nodeLibFile) + argv.push('-Dmodule_root_dir=' + process.cwd()) + argv.push('-Dnode_engine=' + (gyp.opts.node_engine || process.jsEngine || 'v8')) - argv.push('--depth=.') - argv.push('--no-parallel') + argv.push('--depth=.') + argv.push('--no-parallel') - // tell gyp to write the Makefile/Solution files into output_dir - argv.push('--generator-output', outputDir) + // tell gyp to write the Makefile/Solution files into output_dir + argv.push('--generator-output', outputDir) - // tell make to write its output into the same dir - argv.push('-Goutput_dir=.') + // tell make to write its output into the same dir + argv.push('-Goutput_dir=.') - // enforce use of the "binding.gyp" file - argv.unshift('binding.gyp') + // enforce use of the "binding.gyp" file + argv.unshift('binding.gyp') - // execute `gyp` from the current target nodedir - argv.unshift(gypScript) + // execute `gyp` from the current target nodedir + argv.unshift(gypScript) - // make sure python uses files that came with this particular node package - const pypath = [path.join(__dirname, '..', 'gyp', 'pylib')] - if (process.env.PYTHONPATH) { - pypath.push(process.env.PYTHONPATH) - } - process.env.PYTHONPATH = pypath.join(win ? ';' : ':') + // make sure python uses files that came with this particular node package + const pypath = [path.join(__dirname, '..', 'gyp', 'pylib')] + if (process.env.PYTHONPATH) { + pypath.push(process.env.PYTHONPATH) + } + process.env.PYTHONPATH = pypath.join(win ? ';' : ':') + await new Promise((resolve, reject) => { const cp = gyp.spawn(python, argv) - cp.on('exit', onCpExit) + cp.on('exit', (code) => { + if (code !== 0) { + reject(new Error('`gyp` failed with exit code: ' + code)) + } else { + // we're done + resolve() + } + }) }) } - - function onCpExit (code) { - if (code !== 0) { - callback(new Error('`gyp` failed with exit code: ' + code)) - } else { - // we're done - callback() - } - } } /** @@ -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 } diff --git a/lib/create-config-gypi.js b/lib/create-config-gypi.js index 8687379385..a9c427e51d 100644 --- a/lib/create-config-gypi.js +++ b/lib/create-config-gypi.js @@ -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 } diff --git a/lib/find-python.js b/lib/find-python.js index 3288839b50..2f93fdcf02 100644 --- a/lib/find-python.js +++ b/lib/find-python.js @@ -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) - } + // Possible outcomes: + // - Error: not in PATH, not executable or execution fails + // - Gibberish: the next command to check version will fail + // - Absolute path to executable + 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) { - // Possible outcomes: same as checkCommand - if (err) { - this.addLog( - `- "${this.pyLauncher}" is not in PATH or produced an error`) - return errorCallback(err) - } - this.addLog(`- executable path is "${execPath}"`) - this.checkExecPath(execPath, errorCallback) - }.bind(this)) + checkPyLauncher: async function checkPyLauncher () { + this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`) + // Possible outcomes: same as checkCommand + try { + const execPath = await this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false) + this.addLog(`- executable path is "${execPath}"`) + 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) - } + // 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 + 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}`) } - this.succeed(execPath, version) - }.bind(this)) + return this.succeed(execPath, version) + } catch (err) { + this.addLog(`- "${execPath}" could not be run`) + throw err + } }, // 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 = { diff --git a/lib/find-visualstudio.js b/lib/find-visualstudio.js index 45ef7d3fb1..6f8c5ee1e6 100644 --- a/lib/find-visualstudio.js +++ b/lib/find-visualstudio.js @@ -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 - -function findVisualStudio (nodeSemver, configMsvsVersion, callback) { - const finder = new VisualStudioFinder(nodeSemver, configMsvsVersion, - callback) - finder.findVisualStudio() -} +const { existsSync } = require('fs') +const { win32: path } = require('path') +const { regSearchKeys, execFile } = require('./util') -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 } = 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) - } - - const msBuild = path.join(res, 'MSBuild.exe') - this.addLog(`- MSBuild in "${msBuild}"`) - - if (!this.checkConfigVersion(info.versionYear, vsPath)) { - return cb(null) - } - - info.path = vsPath - info.msBuild = msBuild - info.sdk = null - cb(info) - }) - }) + + 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 null + } + + info.path = vsPath + info.msBuild = msBuild + info.sdk = null + 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, diff --git a/lib/install.js b/lib/install.js index 7ac34d2b83..b338102c95 100644 --- a/lib/install.js +++ b/lib/install.js @@ -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, diff --git a/lib/list.js b/lib/list.js index d93370018f..36889ad4f7 100644 --- a/lib/list.js +++ b/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) { - if (err && err.code !== 'ENOENT') { - return callback(err) + let versions = [] + try { + const dir = await fs.readdir(devDir) + if (Array.isArray(dir)) { + versions = dir.filter((v) => v !== 'current') } - - if (Array.isArray(versions)) { - versions = versions.filter(function (v) { return v !== 'current' }) - } else { - versions = [] + } catch (err) { + if (err && err.code !== 'ENOENT') { + throw err } - callback(null, versions) } + + return versions } module.exports = list diff --git a/lib/node-gyp.js b/lib/node-gyp.js index 392a0ecfa6..c2ce69051f 100644 --- a/lib/node-gyp.js +++ b/lib/node-gyp.js @@ -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 diff --git a/lib/rebuild.js b/lib/rebuild.js index a1c5b27cbe..609817665e 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -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 diff --git a/lib/remove.js b/lib/remove.js index 9561cb95e5..7efdb01a66 100644 --- a/lib/remove.js +++ b/lib/remove.js @@ -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' diff --git a/lib/util.js b/lib/util.js index 5950aa25b4..7531a05a8c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,50 +1,57 @@ '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) => { + return new Promise((resolve) => { + const child = cp.execFile(...args, (err, stdout, stderr) => resolve({ err, stdout, stderr })) + 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) { - 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) - } - - 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() + 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) + if (err) { + throw err + } + throw new Error(stderr) + } + + const result = outRe.exec(stdout) + if (!result) { + log.silly('reg', 'error parsing stdout') + throw new Error('Could not parse output of reg.exe') + } + + log.silly('reg', 'found: %j', result[1]) + return result[1] } -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) - }) +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 } diff --git a/test/process-exec-sync.js b/test/process-exec-sync.js deleted file mode 100644 index 0a9002cc80..0000000000 --- a/test/process-exec-sync.js +++ /dev/null @@ -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 diff --git a/test/test-addon.js b/test/test-addon.js index 373647dc4c..0a0b9d56c5 100644 --- a/test/test-addon.js +++ b/test/test-addon.js @@ -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) { diff --git a/test/test-configure-python.js b/test/test-configure-python.js index dcce3e5de2..fd8e8d24ef 100644 --- a/test/test-configure-python.js +++ b/test/test-configure-python.js @@ -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 () => {} } } }) diff --git a/test/test-download.js b/test/test-download.js index 0a13df520b..5b65641f5d 100644 --- a/test/test-download.js +++ b/test/test-download.js @@ -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') diff --git a/test/test-find-python.js b/test/test-find-python.js index e1880bea7a..79bc853552 100644 --- a/test/test-find-python.js +++ b/test/test-find-python.js @@ -4,22 +4,16 @@ 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) { - assert.strictEqual(err, null) - assert.ok(/Python 3/.test(stdout)) - assert.strictEqual(stderr, '') - }) - proc.stdout.setEncoding('utf-8') - proc.stderr.setEncoding('utf-8') - }) + 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, '') }) function poison (object, property) { @@ -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 { stdout: '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 { stdout: '/path/python' } } - f.findPython() - function done (err, python) { - assert.strictEqual(err, null) - assert.strictEqual(python, '/path/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 { stdout: '/path/python' } } else if (/sys\.version_info/.test(args[args.length - 1])) { - cb(null, '2.3.4') + return { stdout: '2.3.4' } } else { assert.fail() } } - f.findPython() - function done (err) { - assert.ok(/Could not find any Python/.test(err)) - assert.ok(/not supported/i.test(f.errorLog)) - } + 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) { - assert.ok(/Could not find any Python/.test(err)) - assert.ok(/not in PATH/.test(f.errorLog)) - } + 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) { - assert.ok(/Could not find any Python/.test(err)) - assert.ok(/not in PATH/.test(f.errorLog)) - } + 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 { stdout: '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 { stdout: '3.9.0' } } else { assert.fail() } @@ -146,58 +136,49 @@ describe('find-python', function () { assert.fail() } } - f.findPython() - - function done (err, python) { - assert.strictEqual(err, null) - assert.strictEqual(python, 'Z:\\snake.exe') - } + 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 { stdout: '3.7.3' } } else { assert.fail() } } - f.findPython() - - function done (err, python) { - assert.strictEqual(err, null) - assert.ok(python === expectedProgram) - } + 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) { - assert.ok(/Could not find any Python/.test(err)) - assert.ok(/not in PATH/.test(f.errorLog)) - } + const { err } = await findPython(f) + assert.ok(/Could not find any Python/.test(err)) + assert.ok(/not in PATH/.test(f.errorLog)) }) }) diff --git a/test/test-find-visualstudio.js b/test/test-find-visualstudio.js index def1fc81f7..cb065e52cf 100644 --- a/test/test-find-visualstudio.js +++ b/test/test-find-visualstudio.js @@ -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,26 +27,22 @@ 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\MSBuild12\\MSBuild.exe', - path: 'C:\\VS2013', - sdk: null, - toolset: 'v120', - version: '12.0', - versionMajor: 12, - versionMinor: 0, - versionYear: 2013 - }) - }) + it('VS2013', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) - finder.findVisualStudio2017OrNewer = (cb) => { - finder.parseData(new Error(), '', '', cb) + finder.findVisualStudio2017OrNewer = async () => { + return finder.parseData(new Error(), '', '') } - 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) { @@ -56,35 +51,44 @@ describe('find-visualstudio', function () { 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\\') + return '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\\') + return 'C:\\MSBuild12\\' default: assert.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() + + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\MSBuild12\\MSBuild.exe', + path: 'C:\\VS2013', + sdk: null, + toolset: 'v120', + version: '12.0', + versionMajor: 12, + versionMinor: 0, + versionYear: 2013 + }) }) - 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,49 +99,51 @@ describe('find-visualstudio', function () { assert.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() - }) - - it('VS2015', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\MSBuild14\\MSBuild.exe', - path: 'C:\\VS2015', - sdk: null, - toolset: 'v140', - version: '14.0', - versionMajor: 14, - versionMinor: 0, - versionYear: 2015 - }) - }) - finder.findVisualStudio2017OrNewer = (cb) => { - finder.parseData(new Error(), '', '', cb) + const { err, info } = await findVisualStudio(finder) + assert.ok(/find .* Visual Studio/i.test(err), 'expect error') + assert.ok(!info, 'no data') + }) + + it('VS2015', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) + + finder.findVisualStudio2017OrNewer = async () => { + return finder.parseData(new Error(), '', '') } - 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) { case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': assert.ok(true, `expected search for registry value ${fullName}`) - return cb(null, 'C:\\VS2015\\VC\\') + return '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\\') + return 'C:\\MSBuild14\\' default: assert.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\MSBuild14\\MSBuild.exe', + path: 'C:\\VS2015', + sdk: null, + toolset: 'v140', + version: '14.0', + versionMajor: 14, + versionMinor: 0, + versionYear: 2015 + }) }) - 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,204 +218,197 @@ describe('find-visualstudio', function () { }) }) - it('minimal VS2017 Build Tools', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + - 'BuildTools\\MSBuild\\15.0\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools', - sdk: '10.0.17134.0', - toolset: 'v141', - version: '15.9.28307.665', - versionMajor: 15, - versionMinor: 9, - versionYear: 2017 - }) - }) + it('minimal VS2017 Build Tools', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_BuildTools_minimal.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() - }) - - it('VS2017 Community with C++ workload', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + - 'Community\\MSBuild\\15.0\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community', - sdk: '10.0.17763.0', - toolset: 'v141', - version: '15.9.28307.665', - versionMajor: 15, - versionMinor: 9, - versionYear: 2017 - }) + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + + 'BuildTools\\MSBuild\\15.0\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools', + sdk: '10.0.17134.0', + toolset: 'v141', + version: '15.9.28307.665', + versionMajor: 15, + versionMinor: 9, + versionYear: 2017 }) + }) + + it('VS2017 Community with C++ workload', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_Community_workload.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() - }) - - it('VS2017 Express', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + - 'WDExpress\\MSBuild\\15.0\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\WDExpress', - sdk: '10.0.17763.0', - toolset: 'v141', - version: '15.9.28307.858', - versionMajor: 15, - versionMinor: 9, - versionYear: 2017 - }) + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + + 'Community\\MSBuild\\15.0\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community', + sdk: '10.0.17763.0', + toolset: 'v141', + version: '15.9.28307.665', + versionMajor: 15, + versionMinor: 9, + versionYear: 2017 }) + }) + + it('VS2017 Express', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2017_Express.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() - }) - - it('VS2019 Preview with C++ workload', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + - 'Preview\\MSBuild\\Current\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Preview', - sdk: '10.0.17763.0', - toolset: 'v142', - version: '16.0.28608.199', - versionMajor: 16, - versionMinor: 0, - versionYear: 2019 - }) + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + + 'WDExpress\\MSBuild\\15.0\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\WDExpress', + sdk: '10.0.17763.0', + toolset: 'v141', + version: '15.9.28307.858', + versionMajor: 15, + versionMinor: 9, + versionYear: 2017 }) + }) + + it('VS2019 Preview with C++ workload', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_Preview.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() - }) - - it('minimal VS2019 Build Tools', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + - 'BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools', - sdk: '10.0.17134.0', - toolset: 'v142', - version: '16.1.28922.388', - versionMajor: 16, - versionMinor: 1, - versionYear: 2019 - }) + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + + 'Preview\\MSBuild\\Current\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Preview', + sdk: '10.0.17763.0', + toolset: 'v142', + version: '16.0.28608.199', + versionMajor: 16, + versionMinor: 0, + versionYear: 2019 }) + }) + + it('minimal VS2019 Build Tools', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_BuildTools_minimal.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() - }) - - it('VS2019 Community with C++ workload', function () { - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + - 'Community\\MSBuild\\Current\\Bin\\MSBuild.exe', - path: - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community', - sdk: '10.0.17763.0', - toolset: 'v142', - version: '16.1.28922.388', - versionMajor: 16, - versionMinor: 1, - versionYear: 2019 - }) + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + + 'BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools', + sdk: '10.0.17134.0', + toolset: 'v142', + version: '16.1.28922.388', + versionMajor: 16, + versionMinor: 1, + versionYear: 2019 }) + }) + + it('VS2019 Community with C++ workload', async function () { + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2019_Community_workload.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + + 'Community\\MSBuild\\Current\\Bin\\MSBuild.exe', + path: + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community', + sdk: '10.0.17763.0', + toolset: 'v142', + version: '16.1.28922.388', + versionMajor: 16, + versionMinor: 1, + versionYear: 2019 + }) }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info, { - msBuild: msBuildPath, - path: - 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community', - sdk: '10.0.22621.0', - toolset: 'v143', - version: '17.4.33213.308', - versionMajor: 17, - versionMinor: 4, - versionYear: 2022 - }) - }) + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') finder.msBuildPathExists = (path) => { return true } - finder.findVisualStudio2017OrNewer = (cb) => { + finder.findVisualStudio2017OrNewer = async () => { const file = path.join(__dirname, 'fixtures', 'VS_2022_Community_workload.txt') const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + return finder.parseData(null, data, '') } - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info, { + msBuild: msBuildPath, + path: + 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community', + sdk: '10.0.22621.0', + toolset: 'v143', + version: '17.4.33213.308', + versionMajor: 17, + versionMinor: 4, + versionYear: 2022 + }) }) 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) => { - assert.ok(/find .* Visual Studio/i.test(err), 'expect error') - assert.ok(!info, 'no data') - }) + it('fail when looking for invalid path', async function () { + const finder = new TestVisualStudioFinder(semverV1, 'AABB') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.ok(/find .* Visual Studio/i.test(err), 'expect error') + assert.ok(!info, 'no data') }) - it('look for VS2013 by version number', function () { - const finder = new TestVisualStudioFinder(semverV1, '2013', (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.versionYear, 2013) - }) + it('look for VS2013 by version number', async function () { + const finder = new TestVisualStudioFinder(semverV1, '2013') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.versionYear, 2013) }) - it('look for VS2013 by installation path', function () { - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013', - (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, 'C:\\VS2013') - }) + it('look for VS2013 by installation path', async function () { + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, 'C:\\VS2013') }) - it('look for VS2015 by version number', function () { - const finder = new TestVisualStudioFinder(semverV1, '2015', (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.versionYear, 2015) - }) + it('look for VS2015 by version number', async function () { + const finder = new TestVisualStudioFinder(semverV1, '2015') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.versionYear, 2015) }) - it('look for VS2015 by installation path', function () { - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015', - (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, 'C:\\VS2015') - }) + it('look for VS2015 by installation path', async function () { + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, 'C:\\VS2015') }) - it('look for VS2017 by version number', function () { - const finder = new TestVisualStudioFinder(semverV1, '2017', (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.versionYear, 2017) - }) + it('look for VS2017 by version number', async function () { + const finder = new TestVisualStudioFinder(semverV1, '2017') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.versionYear, 2017) }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') - }) + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') }) - it('look for VS2019 by version number', function () { - const finder = new TestVisualStudioFinder(semverV1, '2019', (err, info) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.versionYear, 2019) - }) + it('look for VS2019 by version number', async function () { + const finder = new TestVisualStudioFinder(semverV1, '2019') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.versionYear, 2019) }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + 'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, 'C:\\VS2015') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, 'C:\\VS2015') }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) - 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) => { - assert.ok(/find .* Visual Studio/i.test(err), 'expect error') - assert.ok(!info, 'no data') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.ok(/find .* Visual Studio/i.test(err), 'expect error') + assert.ok(!info, 'no data') }) - 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) => { - assert.strictEqual(err, null) - assert.deepStrictEqual(info.path, 'C:\\VS2015') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.strictEqual(err, null) + assert.deepStrictEqual(info.path, 'C:\\VS2015') }) - 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) => { - assert.ok(/find .* Visual Studio/i.test(err), 'expect error') - assert.ok(!info, 'no data') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(finder) - finder.findVisualStudio() + const { err, info } = await findVisualStudio(finder) + assert.ok(/find .* Visual Studio/i.test(err), 'expect error') + assert.ok(!info, 'no data') }) }) diff --git a/test/test-install.js b/test/test-install.js index af83ad9442..3fc39b7c2f 100644 --- a/test/test-install.js +++ b/test/test-install.js @@ -1,52 +1,56 @@ '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 = { - promises: { - stat (_) { - const err = new Error() - err.code = 'EACCES' - assert.ok(true) - throw err + let statCalled = 0 + const mockInstall = createInstall({ + 'graceful-fs': { + promises: { + stat (_) { + const err = new Error() + err.code = 'EACCES' + 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) { + 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) - if (/"pre" versions of node cannot be installed/.test(err.message)) { - assert.ok(true) - } } }) @@ -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) }) })