diff --git a/bin/node-gyp.js b/bin/node-gyp.js index 8652ea21ec..4f8f4b2616 100755 --- a/bin/node-gyp.js +++ b/bin/node-gyp.js @@ -5,105 +5,150 @@ process.title = 'node-gyp' const envPaths = require('env-paths') -const gyp = require('../') +const Gyp = require('../') const log = require('npmlog') const os = require('os') +const fs = require('fs').promises /** * Process and execute the selected commands. */ -const prog = gyp() -var completed = false -prog.parseArgv(process.argv) -prog.devDir = prog.opts.devdir - -var homeDir = os.homedir() -if (prog.devDir) { - prog.devDir = prog.devDir.replace(/^~/, homeDir) -} else if (homeDir) { - prog.devDir = envPaths('node-gyp', { suffix: '' }).cache -} else { - throw new Error( - "node-gyp requires that the user's home directory is specified " + - 'in either of the environmental variables HOME or USERPROFILE. ' + - 'Overide with: --devdir /path/to/.node-gyp') +let completed = false + +async function run () { + const gyp = new Gyp() + gyp.parseArgv(process.argv) + + printVersion(gyp) + setupDevDir(gyp) + + log.info('it worked if it ends with', 'ok') + log.verbose('cli', process.argv) + log.info('using', `node-gyp@${gyp.version}`) + log.info('using', `node@${process.versions.node} | ${process.platform} | ${process.arch}`) + + await chdir(gyp) + await execute(gyp) } -if (prog.todo.length === 0) { - if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) { - console.log('v%s', prog.version) - } else { - console.log('%s', prog.usage()) +function printVersion (gyp) { + if (gyp.todo.length === 0) { + if (process.argv.includes('-v') || process.argv.includes('--version')) { + console.log('v%s', gyp.version) + } else { + console.log('%s', gyp.usage()) + } + process.exit(0) } - process.exit(0) } -log.info('it worked if it ends with', 'ok') -log.verbose('cli', process.argv) -log.info('using', 'node-gyp@%s', prog.version) -log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch) +function setupDevDir (gyp) { + gyp.devDir = gyp.opts.devdir + + const homeDir = os.homedir() + if (gyp.devDir) { + gyp.devDir = gyp.devDir.replace(/^~/, homeDir) + } else if (homeDir) { + gyp.devDir = envPaths('node-gyp', { suffix: '' }).cache + } else { + throw new Error( + 'node-gyp requires that the user\'s home directory is specified ' + + 'in either of the environmental variables HOME or USERPROFILE. ' + + 'Overide with: --devdir /path/to/.node-gyp') + } +} /** * Change dir if -C/--directory was passed. */ - -var dir = prog.opts.directory -if (dir) { - var fs = require('fs') - try { - var stat = fs.statSync(dir) - if (stat.isDirectory()) { - log.info('chdir', dir) - process.chdir(dir) - } else { - log.warn('chdir', dir + ' is not a directory') - } - } catch (e) { - if (e.code === 'ENOENT') { - log.warn('chdir', dir + ' is not a directory') - } else { - log.warn('chdir', 'error during chdir() "%s"', e.message) +async function chdir (gyp) { + const dir = gyp.opts.directory + if (dir) { + try { + const stat = await fs.stat(dir) + if (stat.isDirectory()) { + log.info('chdir', dir) + process.chdir(dir) + } else { + log.warn('chdir', dir + ' is not a directory') + } + } catch (e) { + if (e.code === 'ENOENT') { + log.warn('chdir', dir + ' is not a directory') + } else { + log.warn('chdir', 'error during chdir() "%s"', e.message) + } } } } -function run () { - var command = prog.todo.shift() - if (!command) { - // done! - completed = true - log.info('ok') - 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) +const asyncCommands = [ + 'clean' +] + +async function execute (gyp) { + while (true) { + const command = gyp.todo.shift() + if (!command) { + // done! + completed = true + log.info('ok') + return } - if (command.name === 'list') { - var versions = arguments[1] - if (versions.length > 0) { - versions.forEach(function (version) { - console.log(version) - }) - } else { - console.log('No node development files installed. Use `node-gyp install` to install a version.') + + if (asyncCommands.includes(command.name)) { + try { + const result = await gyp.commands[command.name](command.args) + + if (command.name === 'list') { + const versions = result[0] + if (versions.length > 0) { + versions.forEach((version) => console.log(version)) + } else { + console.log('No node development files installed. Use `node-gyp install` to install a version.') + } + } else if (Array.isArray(result) && result.length) { + console.log.apply(console, result) + } + } catch (err) { + log.error(command.name + ' error') + log.error('stack', err.stack) + errorMessage() + log.error('not ok') + return process.exit(1) } - } else if (arguments.length >= 2) { - console.log.apply(console, [].slice.call(arguments, 1)) + } else { + // TODO: removeme + await new Promise((resolve, reject) => { + gyp.commands[command.name](command.args, (err, ...args) => { + if (err) { + log.error(command.name + ' error') + log.error('stack', err.stack) + errorMessage() + log.error('not ok') + return process.exit(1) + } + + if (command.name === 'list') { + const versions = args[0] + if (versions.length > 0) { + versions.forEach((version) => console.log(version)) + } else { + console.log('No node development files installed. Use `node-gyp install` to install a version.') + } + } else if (args.length >= 1) { + console.log.apply(console, args) + } + + resolve() + }) + }) } - - // now run the next command in the queue - process.nextTick(run) - }) + } } -process.on('exit', function (code) { +process.on('exit', (code) => { if (!completed && !code) { log.error('Completion callback never invoked!') issueMessage() @@ -111,7 +156,7 @@ process.on('exit', function (code) { } }) -process.on('uncaughtException', function (err) { +process.on('uncaughtException', (err) => { log.error('UNCAUGHT EXCEPTION') log.error('stack', err.stack) issueMessage() @@ -120,21 +165,22 @@ process.on('uncaughtException', function (err) { function errorMessage () { // copied from npm's lib/utils/error-handler.js - var os = require('os') - log.error('System', os.type() + ' ' + os.release()) - log.error('command', process.argv - .map(JSON.stringify).join(' ')) + const os = require('os') + log.error('System', `${os.type()}${os.release()}`) + log.error('command', process.argv.map(JSON.stringify).join(' ')) log.error('cwd', process.cwd()) log.error('node -v', process.version) - log.error('node-gyp -v', 'v' + prog.package.version) + log.error('node-gyp -v', `v${require('../package.json').version}`) } function issueMessage () { errorMessage() - log.error('', ['Node-gyp failed to build your package.', - 'Try to update npm and/or node-gyp and if it does not help file an issue with the package author.' - ].join('\n')) + log.error('', +`node-gyp failed to build your package. +Try to update npm and/or node-gyp and if it does not help file an issue with the package author.`) } -// start running the given commands! -run() +run().catch((err) => { + console.error(err.stack) + process.exit(1) +}) diff --git a/lib/args.js b/lib/args.js new file mode 100644 index 0000000000..c00283d4e8 --- /dev/null +++ b/lib/args.js @@ -0,0 +1,120 @@ +const nopt = require('nopt') +const log = require('npmlog') + +const commands = [ + // Module build commands + 'build', + 'clean', + 'configure', + 'rebuild', + // Development Header File management commands + 'install', + 'list', + 'remove' +] + +const aliases = { + ls: 'list', + rm: 'remove' +} + +const shorthands = { + release: '--no-debug', + C: '--directory', + debug: '--debug', + j: '--jobs', + silly: '--loglevel=silly', + verbose: '--loglevel=verbose', + silent: '--loglevel=silent' +} + +const configDefs = { + help: Boolean, // everywhere + arch: String, // 'configure' + cafile: String, // 'install' + debug: Boolean, // 'build' + directory: String, // bin + make: String, // 'build' + msvs_version: String, // 'configure' + ensure: Boolean, // 'install' + solution: String, // 'build' (windows only) + proxy: String, // 'install' + noproxy: String, // 'install' + devdir: String, // everywhere + nodedir: String, // 'configure' + loglevel: String, // everywhere + python: String, // 'configure' + 'dist-url': String, // 'install' + tarball: String, // 'install' + jobs: String, // 'build' + thin: String // 'configure' +} + +/** + * Parses the given argv array and sets the 'opts', + * 'argv' and 'command' properties. + */ +function parseArgv (argv) { + const opts = nopt(configDefs, shorthands, argv) + argv = opts.argv.remain.slice() + + const todo = [] + + // create a copy of the argv array with aliases mapped + argv = argv.map((arg) => { + // is this an alias? + if (arg in aliases) { + arg = aliases[arg] + } + return arg + }) + + // process the mapped args into "command" objects ("name" and "args" props) + argv.slice().forEach((arg) => { + if (commands.includes(arg)) { + const args = argv.splice(0, argv.indexOf(arg)) + argv.shift() + if (todo.length > 0) { + todo[todo.length - 1].args = args + } + todo.push({ name: arg, args: [] }) + } + }) + if (todo.length > 0) { + todo[todo.length - 1].args = argv.splice(0) + } + + // support for inheriting config env variables from npm + const npmConfigPrefix = 'npm_config_' + Object.keys(process.env).forEach((name) => { + if (name.indexOf(npmConfigPrefix) !== 0) { + return + } + const val = process.env[name] + if (name === npmConfigPrefix + 'loglevel') { + log.level = val + } else { + // add the user-defined options to the config + name = name.substring(npmConfigPrefix.length) + // gyp@741b7f1 enters an infinite loop when it encounters + // zero-length options so ensure those don't get through. + if (name) { + opts[name] = val + } + } + }) + + if (opts.loglevel) { + log.level = opts.loglevel + } + + log.resume() + + return { opts, argv, todo } +} + +module.exports = parseArgv +module.exports.commands = commands +module.exports.aliases = aliases +module.exports.shorthands = shorthands +module.exports.configDefs = configDefs diff --git a/lib/clean.js b/lib/clean.js index dbfa4dbb99..994ca0e1a3 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -1,15 +1,17 @@ 'use strict' -const rm = require('rimraf') +const usage = 'Removes any generated build files and the "out" dir' + const log = require('npmlog') +const { promisify } = require('util') +const rm = promisify(require('rimraf')) -function clean (gyp, argv, callback) { +async function clean (gyp, argv) { // Remove the 'build' dir - var buildDir = 'build' - + const buildDir = 'build' log.verbose('clean', 'removing "%s" directory', buildDir) - rm(buildDir, callback) + await rm(buildDir) } module.exports = clean -module.exports.usage = 'Removes any generated build files and the "out" dir' +module.exports.usage = usage diff --git a/lib/configure.js b/lib/configure.js index 564564eea4..00a0dd8c6a 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -1,35 +1,33 @@ 'use strict' +const win = process.platform === 'win32' +const usage = `Generates ${win ? 'MSVC project files' : 'a Makefile'} for the current module` + const fs = require('graceful-fs') const path = require('path') const log = require('npmlog') const os = require('os') const mkdirp = require('mkdirp') const processRelease = require('./process-release') -const win = process.platform === 'win32' const findNodeDirectory = require('./find-node-directory') const msgFormat = require('util').format -var findPython = require('./find-python') -if (win) { - var findVisualStudio = require('./find-visualstudio') -} +const findPython = require('./find-python') +const findVisualStudio = win && require('./find-visualstudio') function configure (gyp, argv, callback) { - var python - var buildDir = path.resolve('build') - var configNames = ['config.gypi', 'common.gypi'] - var configs = [] - var nodeDir - var release = processRelease(argv, gyp, process.version, process.release) - - findPython(gyp.opts.python, function (err, found) { - if (err) { - callback(err) - } else { + let python + 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) + .catch(callback) + .then((found) => { python = found getNodeDir() - } - }) + }) function getNodeDir () { // 'python' should be set by now @@ -60,7 +58,7 @@ function configure (gyp, argv, callback) { // into devdir. Otherwise only install if they're not already there. gyp.opts.ensure = !gyp.opts.tarball - gyp.commands.install([release.version], function (err) { + gyp.commands.install([release.version], (err) => { if (err) { return callback(err) } @@ -73,7 +71,7 @@ function configure (gyp, argv, callback) { function createBuildDir () { log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir) - mkdirp(buildDir, function (err, isNew) { + mkdirp(buildDir, (err, isNew) => { if (err) { return callback(err) } @@ -92,14 +90,14 @@ function configure (gyp, argv, callback) { return callback(err) } - var configFilename = 'config.gypi' - var configPath = path.resolve(buildDir, configFilename) + const configFilename = 'config.gypi' + const configPath = path.resolve(buildDir, configFilename) log.verbose('build/' + configFilename, 'creating config file') - var config = process.config || {} - var defaults = config.target_defaults - var variables = config.variables + const config = process.config || {} + let defaults = config.target_defaults + let variables = config.variables // default "config.variables" if (!variables) { @@ -164,7 +162,7 @@ function configure (gyp, argv, callback) { // this allows for module-specific configure flags like: // // $ node-gyp configure --shared-libxml2 - Object.keys(gyp.opts).forEach(function (opt) { + Object.keys(gyp.opts).forEach((opt) => { if (opt === 'argv') { return } @@ -174,20 +172,14 @@ function configure (gyp, argv, callback) { variables[opt.replace(/-/g, '_')] = gyp.opts[opt] }) - // ensures that any boolean values from `process.config` get stringified - function boolsToString (k, v) { - if (typeof v === 'boolean') { - return String(v) - } - return v - } - log.silly('build/' + configFilename, config) // now write out the config.gypi file to the build/ dir - var prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step' + const prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step' - var json = JSON.stringify(config, boolsToString, 2) + // ensures that any boolean values from `process.config` get stringified + const boolsToString = (k, v) => typeof v === 'boolean' ? String(v) : v + const json = JSON.stringify(config, boolsToString, 2) log.verbose('build/' + configFilename, 'writing out config file: %s', configPath) configs.push(configPath) fs.writeFile(configPath, [prefix, json, ''].join('\n'), findConfigs) @@ -198,14 +190,14 @@ function configure (gyp, argv, callback) { return callback(err) } - var name = configNames.shift() + const name = configNames.shift() if (!name) { return runGyp() } - var fullPath = path.resolve(name) + const fullPath = path.resolve(name) log.verbose(name, 'checking for gypi file: %s', fullPath) - fs.stat(fullPath, function (err) { + fs.stat(fullPath, (err) => { if (err) { if (err.code === 'ENOENT') { findConfigs() // check next gypi filename @@ -238,17 +230,15 @@ function configure (gyp, argv, callback) { } // include all the ".gypi" files that were found - configs.forEach(function (config) { - argv.push('-I', config) - }) + configs.forEach((config) => argv.push('-I', config)) // For AIX and z/OS we need to set up the path to the exports file // which contains the symbols needed for linking. - var nodeExpFile + let nodeExpFile if (process.platform === 'aix' || process.platform === 'os390') { - var ext = process.platform === 'aix' ? 'exp' : 'x' - var nodeRootDir = findNodeDirectory() - var candidates + const ext = process.platform === 'aix' ? 'exp' : 'x' + const nodeRootDir = findNodeDirectory() + let candidates if (process.platform === 'aix') { candidates = [ @@ -256,47 +246,43 @@ function configure (gyp, argv, callback) { 'out/Release/node', 'out/Debug/node', 'node' - ].map(function (file) { - return file + '.' + ext - }) + ].map((file) => `${file}.${ext}`) } else { candidates = [ 'out/Release/obj.target/libnode', 'out/Debug/obj.target/libnode', 'lib/libnode' - ].map(function (file) { - return file + '.' + ext - }) + ].map((file) => `${file}.${ext}`) } - var logprefix = 'find exports file' + const logprefix = 'find exports file' nodeExpFile = findAccessibleSync(logprefix, nodeRootDir, candidates) if (nodeExpFile !== undefined) { log.verbose(logprefix, 'Found exports file: %s', nodeExpFile) } else { - var msg = msgFormat('Could not find node.%s file in %s', ext, nodeRootDir) + 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)) } } // this logic ported from the old `gyp_addon` python file - var gypScript = path.resolve(__dirname, '..', 'gyp', 'gyp_main.py') - var addonGypi = path.resolve(__dirname, '..', 'addon.gypi') - var commonGypi = path.resolve(nodeDir, 'include/node/common.gypi') - fs.stat(commonGypi, function (err) { + 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, (err) => { if (err) { commonGypi = path.resolve(nodeDir, 'common.gypi') } - var outputDir = 'build' + let outputDir = 'build' if (win) { // Windows expects an absolute path outputDir = buildDir } - var nodeGypDir = path.resolve(__dirname, '..') + const nodeGypDir = path.resolve(__dirname, '..') - var nodeLibFile = path.join(nodeDir, + let nodeLibFile = path.join(nodeDir, !gyp.opts.nodedir ? '<(target_arch)' : '$(Configuration)', release.name + '.lib') @@ -335,13 +321,13 @@ function configure (gyp, argv, callback) { argv.unshift(gypScript) // make sure python uses files that came with this particular node package - var pypath = [path.join(__dirname, '..', 'gyp', 'pylib')] + const pypath = [path.join(__dirname, '..', 'gyp', 'pylib')] if (process.env.PYTHONPATH) { pypath.push(process.env.PYTHONPATH) } process.env.PYTHONPATH = pypath.join(win ? ';' : ':') - var cp = gyp.spawn(python, argv) + const cp = gyp.spawn(python, argv) cp.on('exit', onCpExit) }) } @@ -362,18 +348,19 @@ function configure (gyp, argv, callback) { * readable. */ function findAccessibleSync (logprefix, dir, candidates) { - for (var next = 0; next < candidates.length; next++) { - var candidate = path.resolve(dir, candidates[next]) + for (const candidate of candidates) { + const candidatePath = path.resolve(dir, candidate) + let fd try { - var fd = fs.openSync(candidate, 'r') + fd = fs.openSync(candidatePath, 'r') } catch (e) { // this candidate was not found or not readable, do nothing - log.silly(logprefix, 'Could not open %s: %s', candidate, e.message) + log.silly(logprefix, 'Could not open %s: %s', candidatePath, e.message) continue } fs.closeSync(fd) - log.silly(logprefix, 'Found readable %s', candidate) - return candidate + log.silly(logprefix, 'Found readable %s', candidatePath) + return candidatePath } return undefined @@ -383,4 +370,4 @@ module.exports = configure module.exports.test = { findAccessibleSync: findAccessibleSync } -module.exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module' +module.exports.usage = usage diff --git a/lib/find-node-directory.js b/lib/find-node-directory.js index 0dd781a6cf..92518a4fd8 100644 --- a/lib/find-node-directory.js +++ b/lib/find-node-directory.js @@ -14,18 +14,16 @@ function findNodeDirectory (scriptLocation, processObj) { } // Have a look to see what is above us, to try and work out where we are - var npmParentDirectory = path.join(scriptLocation, '../../../..') - log.verbose('node-gyp root', 'npm_parent_directory is ' + - path.basename(npmParentDirectory)) - var nodeRootDir = '' + const npmParentDirectory = path.join(scriptLocation, '../../../..') + log.verbose('node-gyp root', `npm_parent_directory is ${path.basename(npmParentDirectory)}`) + let nodeRootDir = '' log.verbose('node-gyp root', 'Finding node root directory') if (path.basename(npmParentDirectory) === 'deps') { // We are in a build directory where this script lives in // deps/npm/node_modules/node-gyp/lib nodeRootDir = path.join(npmParentDirectory, '..') - log.verbose('node-gyp root', 'in build directory, root = ' + - nodeRootDir) + log.verbose('node-gyp root', `in build directory, root = ${nodeRootDir}`) } else if (path.basename(npmParentDirectory) === 'node_modules') { // We are in a node install directory where this script lives in // lib/node_modules/npm/node_modules/node-gyp/lib or @@ -36,13 +34,12 @@ function findNodeDirectory (scriptLocation, processObj) { } else { nodeRootDir = path.join(npmParentDirectory, '../..') } - log.verbose('node-gyp root', 'in install directory, root = ' + - nodeRootDir) + log.verbose('node-gyp root', `in install directory, root = ${nodeRootDir}`) } else { // We don't know where we are, try working it out from the location // of the node binary - var nodeDir = path.dirname(processObj.execPath) - var directoryUp = path.basename(nodeDir) + const nodeDir = path.dirname(processObj.execPath) + const directoryUp = path.basename(nodeDir) if (directoryUp === 'bin') { nodeRootDir = path.join(nodeDir, '..') } else if (directoryUp === 'Release' || directoryUp === 'Debug') { diff --git a/lib/find-python.js b/lib/find-python.js index 43bf85985a..e4676c51ac 100644 --- a/lib/find-python.js +++ b/lib/find-python.js @@ -5,70 +5,62 @@ const log = require('npmlog') const semver = require('semver') const cp = require('child_process') const extend = require('util')._extend // eslint-disable-line +const { logWithPrefix } = require('./util') const win = process.platform === 'win32' -const logWithPrefix = require('./util').logWithPrefix -function PythonFinder (configPython, callback) { - this.callback = callback - this.configPython = configPython - this.errorLog = [] -} +class PythonFinder { + constructor (configPython, callback) { + this.callback = callback + this.configPython = configPython + this.errorLog = [] -PythonFinder.prototype = { - log: logWithPrefix(log, 'find Python'), - argsExecutable: ['-c', 'import sys; print(sys.executable);'], - argsVersion: ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'], - semverRange: '2.7.x || >=3.5.0', + this.log = logWithPrefix(log, 'find Python') + this.argsExecutable = ['-c', 'import sys; print(sys.executable);'] + this.argsVersion = ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'] + this.semverRange = '2.7.x || >=3.5.0' - // These can be overridden for testing: - execFile: cp.execFile, - env: process.env, - win: win, - pyLauncher: 'py.exe', - winDefaultLocations: [ - path.join(process.env.SystemDrive || 'C:', 'Python37', 'python.exe'), - path.join(process.env.SystemDrive || 'C:', 'Python27', 'python.exe') - ], + // These can be overridden for testing: + this.execFile = cp.execFile + this.env = process.env + this.win = win + this.pyLauncher = 'py.exe' + this.winDefaultLocations = [ + path.join(process.env.SystemDrive || 'C:\\Python37\\python.exe'), + path.join(process.env.SystemDrive || 'C:\\Python27\\python.exe') + ] + } // Logs a message at verbose level, but also saves it to be displayed later // at error level if an error occurs. This should help diagnose the problem. - addLog: function addLog (message) { + addLog (message) { this.log.verbose(message) this.errorLog.push(message) - }, + } // 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 - var toCheck = getChecks.apply(this) - - function getChecks () { + findPython () { + const toCheck = (() => { if (this.env.NODE_GYP_FORCE_PYTHON) { return [{ before: () => { - this.addLog( - 'checking Python explicitly set from NODE_GYP_FORCE_PYTHON') - this.addLog('- process.env.NODE_GYP_FORCE_PYTHON is ' + - `"${this.env.NODE_GYP_FORCE_PYTHON}"`) + this.addLog('checking Python explicitly set from NODE_GYP_FORCE_PYTHON') + 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 }] } - var checks = [ + const checks = [ { before: () => { if (!this.configPython) { - this.addLog( - 'Python is not set from command line or npm configuration') - return SKIP + this.addLog('Python is not set from command line or npm configuration') + return 'skip' } - this.addLog('checking Python explicitly set from command line or ' + - 'npm configuration') - this.addLog('- "--python=" or "npm config get python" is ' + - `"${this.configPython}"`) + this.addLog('checking Python explicitly set from command line or npm configuration') + this.addLog(`- "--python=" or "npm config get python" is "${this.configPython}"`) }, check: this.checkCommand, arg: this.configPython @@ -76,91 +68,79 @@ PythonFinder.prototype = { { before: () => { if (!this.env.PYTHON) { - this.addLog('Python is not set from environment variable ' + - 'PYTHON') - return SKIP + this.addLog('Python is not set from environment variable PYTHON') + return 'skip' } - this.addLog('checking Python explicitly set from environment ' + - 'variable PYTHON') + this.addLog('checking Python explicitly set from environment variable PYTHON') this.addLog(`- process.env.PYTHON is "${this.env.PYTHON}"`) }, check: this.checkCommand, arg: this.env.PYTHON }, { - before: () => { this.addLog('checking if "python3" can be used') }, + before: () => this.addLog('checking if "python3" can be used'), check: this.checkCommand, arg: 'python3' }, { - before: () => { this.addLog('checking if "python" can be used') }, + before: () => this.addLog('checking if "python" can be used'), check: this.checkCommand, arg: 'python' }, { - before: () => { this.addLog('checking if "python2" can be used') }, + before: () => this.addLog('checking if "python2" can be used'), check: this.checkCommand, arg: 'python2' } ] if (this.win) { - for (var i = 0; i < this.winDefaultLocations.length; ++i) { + for (let i = 0; i < this.winDefaultLocations.length; ++i) { const location = this.winDefaultLocations[i] checks.push({ - before: () => { - this.addLog('checking if Python is ' + - `${location}`) - }, + before: () => this.addLog(`checking if Python is ${location}`), check: this.checkExecPath, arg: location }) } checks.push({ - before: () => { - this.addLog( - 'checking if the py launcher can be used to find Python 2') - }, + before: () => this.addLog('checking if the py launcher can be used to find Python 2'), check: this.checkPyLauncher }) } return checks - } - - function runChecks (err) { - this.log.silly('runChecks: err = %j', (err && err.stack) || err) + })() + const runChecks = async () => { const check = toCheck.shift() if (!check) { return this.fail() } - const before = check.before.apply(this) - if (before === SKIP) { - return runChecks.apply(this) - } - if (before === FAIL) { - return this.fail() + const before = check.before() + if (before === 'skip') { + return runChecks() } - const args = [runChecks.bind(this)] - if (check.arg) { - args.unshift(check.arg) + try { + return await check.check.call(this, check.arg ? check.arg : undefined) + } catch (err) { + this.log.silly('runChecks: err = %j', (err && err.stack) || err) + return runChecks() } - check.check.apply(this, args) } - runChecks.apply(this) - }, + return runChecks() + } // 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) { - var exec = command - var args = this.argsExecutable - var shell = false + async checkCommand (command) { + let exec = command + let args = this.argsExecutable + let shell = false if (this.win) { // Arguments have to be manually quoted exec = `"${exec}"` @@ -169,19 +149,20 @@ PythonFinder.prototype = { } this.log.verbose(`- executing "${command}" to get executable path`) - this.run(exec, args, shell, function (err, execPath) { + let execPath + try { + execPath = await this.run(exec, args, shell) // 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) - } this.addLog(`- executable path is "${execPath}"`) - this.checkExecPath(execPath, errorCallback) - }.bind(this)) - }, + } catch (err) { + this.addLog(`- "${command}" is not in PATH or produced an error`) + throw err + } + return this.checkExecPath(execPath) + } // Check if the py launcher can find a valid Python to use. // Will exit the Python finder on success. @@ -193,63 +174,63 @@ PythonFinder.prototype = { // the first command line argument. Since "py.exe -2" 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 2 executable path`) - this.run(this.pyLauncher, ['-2', ...this.argsExecutable], false, - function (err, execPath) { + async checkPyLauncher () { + this.log.verbose(`- executing "${this.pyLauncher}" to get Python 2 executable path`) + let execPath + try { + execPath = await this.run(this.pyLauncher, ['-2', ...this.argsExecutable], false) // 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)) - }, + } catch (err) { + this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`) + throw err + } + this.addLog(`- executable path is "${execPath}"`) + return this.checkExecPath(execPath) + } // Check if a Python executable is the correct version to use. // Will exit the Python finder on success. - checkExecPath: function checkExecPath (execPath, errorCallback) { + async checkExecPath (execPath) { this.log.verbose(`- executing "${execPath}" to get version`) - this.run(execPath, this.argsVersion, false, function (err, version) { + let version + try { + version = await this.run(execPath, this.argsVersion, false) // 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) - } - this.addLog(`- version is "${version}"`) + } catch (err) { + this.addLog(`- "${execPath}" could not be run`) + throw err + } - const range = new semver.Range(this.semverRange) - var valid = false - try { - valid = range.test(version) - } catch (err) { - 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) - } + this.addLog(`- version is "${version}"`) - 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}`)) - } - this.succeed(execPath, version) - }.bind(this)) - }, + const range = new semver.Range(this.semverRange) + let valid = false + try { + valid = range.test(version) + } catch (err) { + 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?') + throw err + } + + if (!valid) { + this.addLog(`- version is ${version} - should be ${this.semverRange}`) + this.addLog('- THIS VERSION OF PYTHON IS NOT SUPPORTED') + throw new Error(`Found unsupported Python version ${version}`) + } + + return this.succeed(execPath, version) + } // Run an executable or shell command, trimming the output. - run: function run (exec, args, shell, callback) { - var env = extend({}, this.env) + async run (exec, args, shell) { + const env = extend({}, this.env) env.TERM = 'dumb' const opts = { env: env, shell: shell } @@ -257,61 +238,59 @@ 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)) + const stdout = await new Promise((resolve, reject) => { + this.execFile(exec, args, opts, (err, stdout, stderr) => { + if (err) { + err.stdout = stdout + err.stderr = stderr + reject(err) + } + resolve(stdout) + }) + }) + const execPath = stdout.trim() + return execPath } catch (err) { - this.log.silly('execFile: threw:\n%s', err.stack) - return callback(err) - } - - function execFileCallback (err, stdout, stderr) { 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) + this.log.silly('execFile result: stdout = %j', err.stdout) + this.log.silly('execFile result: stderr = %j', err.stderr) + throw err } - }, + } - succeed: function succeed (execPath, version) { + 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 () { + fail () { const errorLog = this.errorLog.join('\n') - const pathExample = this.win ? 'C:\\Path\\To\\python.exe' - : '/path/to/pythonexecutable' + const pathExample = this.win ? 'C:\\Path\\To\\python.exe' : '/path/to/pythonexecutable' // For Windows 80 col console, use up to the column before the one marked // with X (total 79 chars including logger prefix, 58 chars usable here): - // X - const info = [ - '**********************************************************', - 'You need to install the latest version of Python.', - 'Node-gyp should be able to find and use Python. If not,', - 'you can try one of the following options:', - `- Use the switch --python="${pathExample}"`, - ' (accepted by both node-gyp and npm)', - '- Set the environment variable PYTHON', - '- Set the npm configuration variable python:', - ` npm config set python "${pathExample}"`, - 'For more information consult the documentation at:', - 'https://github.com/nodejs/node-gyp#installation', - '**********************************************************' - ].join('\n') - + const info = +`********************************************************** +You need to install the latest version of Python. +Node-gyp should be able to find and use Python. If not, +you can try one of the following options: +- Use the switch --python="${pathExample}" + (accepted by both node-gyp and npm) +- Set the environment variable PYTHON +- Set the npm configuration variable python: + npm config set python "${pathExample}" +For more information consult the documentation at: +https://github.com/nodejs/node-gyp#installation +********************************************************** +` 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) { - var finder = new PythonFinder(configPython, callback) - finder.findPython() +async function findPython (configPython, callback) { + const finder = new PythonFinder(configPython, callback) + return finder.findPython() } module.exports = findPython diff --git a/lib/find-visualstudio.js b/lib/find-visualstudio.js index c5d26f9a20..4b1011d2a0 100644 --- a/lib/find-visualstudio.js +++ b/lib/find-visualstudio.js @@ -1,10 +1,9 @@ 'use strict' const log = require('npmlog') -const execFile = require('child_process').execFile -const path = require('path').win32 -const logWithPrefix = require('./util').logWithPrefix -const regSearchKeys = require('./util').regSearchKeys +const { execFile } = require('child_process') +const { win32: path } = require('path') +const { logWithPrefix, regSearchKeys } = require('./util') function findVisualStudio (nodeSemver, configMsvsVersion, callback) { const finder = new VisualStudioFinder(nodeSemver, configMsvsVersion, @@ -12,79 +11,72 @@ function findVisualStudio (nodeSemver, configMsvsVersion, callback) { finder.findVisualStudio() } -function VisualStudioFinder (nodeSemver, configMsvsVersion, callback) { - this.nodeSemver = nodeSemver - this.configMsvsVersion = configMsvsVersion - this.callback = callback - this.errorLog = [] - this.validVersions = [] -} - -VisualStudioFinder.prototype = { - log: logWithPrefix(log, 'find VS'), +class VisualStudioFinder { + constructor (nodeSemver, configMsvsVersion, callback) { + this.nodeSemver = nodeSemver + this.configMsvsVersion = configMsvsVersion + this.callback = callback + this.errorLog = [] + this.validVersions = [] - regSearchKeys: regSearchKeys, + this.log = logWithPrefix(log, 'find VS') + this.regSearchKeys = regSearchKeys + } // Logs a message at verbose level, but also saves it to be displayed later // at error level if an error occurs. This should help diagnose the problem. - addLog: function addLog (message) { + addLog (message) { this.log.verbose(message) this.errorLog.push(message) - }, + } - findVisualStudio: function findVisualStudio () { + async findVisualStudio () { this.configVersionYear = null this.configPath = null if (this.configMsvsVersion) { this.addLog('msvs_version was set from command line or npm config') if (this.configMsvsVersion.match(/^\d{4}$/)) { this.configVersionYear = parseInt(this.configMsvsVersion, 10) - this.addLog( - `- looking for Visual Studio version ${this.configVersionYear}`) + this.addLog(`- looking for Visual Studio version ${this.configVersionYear}`) } else { this.configPath = path.resolve(this.configMsvsVersion) - this.addLog( - `- looking for Visual Studio installed in "${this.configPath}"`) + this.addLog(`- looking for Visual Studio installed in "${this.configPath}"`) } } else { this.addLog('msvs_version not set from command line or npm config') } if (process.env.VCINSTALLDIR) { - this.envVcInstallDir = - path.resolve(process.env.VCINSTALLDIR, '..') + this.envVcInstallDir = path.resolve(process.env.VCINSTALLDIR, '..') this.addLog('running in VS Command Prompt, installation path is:\n' + `"${this.envVcInstallDir}"\n- will only use this version`) } else { this.addLog('VCINSTALLDIR not set, not running in VS Command Prompt') } - this.findVisualStudio2017OrNewer((info) => { - 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() - }) - }) - }) - }, + let info = await this.findVisualStudio2017OrNewer() + if (info) { + return this.succeed(info) + } + info = await this.findVisualStudio2015() + if (info) { + return this.succeed(info) + } + info = await this.findVisualStudio2013() + if (info) { + return this.succeed(info) + } + this.fail() + } - succeed: function succeed (info) { + 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 () { + fail () { if (this.configMsvsVersion && this.envVcInstallDir) { this.errorLog.push( 'msvs_version does not match this VS Command Prompt or the', @@ -118,49 +110,51 @@ 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) { - var ps = path.join(process.env.SystemRoot, 'System32', - 'WindowsPowerShell', 'v1.0', 'powershell.exe') - var csFile = path.join(__dirname, 'Find-VisualStudio.cs') - var psArgs = [ + async findVisualStudio2017OrNewer () { + const ps = path.join(process.env.SystemRoot, + 'System32\\WindowsPowerShell\\v1.0\\powershell.exe') + const csFile = path.join(__dirname, 'Find-VisualStudio.cs') + const psArgs = [ '-ExecutionPolicy', 'Unrestricted', '-NoProfile', '-Command', - '&{Add-Type -Path \'' + csFile + '\';' + '[VisualStudioConfiguration.Main]::PrintJson()}' + `&{Add-Type -Path '${csFile}';[VisualStudioConfiguration.Main]::PrintJson()}` ] this.log.silly('Running', ps, psArgs) - var child = execFile(ps, psArgs, { encoding: 'utf8' }, - (err, stdout, stderr) => { - this.parseData(err, stdout, stderr, cb) + const { stdout, stderr } = await new Promise((resolve, reject) => { + const child = execFile(ps, psArgs, { encoding: 'utf8' }, (err, stdout, stderr) => { + if (err) { + this.log.silly('PS err = %j', err && (err.stack || err)) + this.addLog('could not use PowerShell to find Visual Studio 2017 or newer') + return reject(err) + } + resolve({ stdout, stderr }) }) - child.stdin.end() - }, + child.stdin.end() + }) + + return this.parseData(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 (stdout, stderr) { this.log.silly('PS stderr = %j', stderr) const failPowershell = () => { - this.addLog( - 'could not use PowerShell to find Visual Studio 2017 or newer') - cb(null) - } - - if (err) { - this.log.silly('PS err = %j', err && (err.stack || err)) - return failPowershell() + this.addLog('could not use PowerShell to find Visual Studio 2017 or newer') + return null } - var vsInfo + let vsInfo try { vsInfo = JSON.parse(stdout) } catch (e) { @@ -177,7 +171,7 @@ VisualStudioFinder.prototype = { vsInfo = vsInfo.map((info) => { this.log.silly(`processing installation: "${info.path}"`) info.path = path.resolve(info.path) - var ret = this.getVersionInfo(info) + const ret = this.getVersionInfo(info) ret.path = info.path ret.msBuild = this.getMSBuild(info, ret.versionYear) ret.toolset = this.getToolset(info, ret.versionYear) @@ -198,10 +192,10 @@ VisualStudioFinder.prototype = { // Sort to place newer versions first vsInfo.sort((a, b) => b.versionYear - a.versionYear) - for (var i = 0; i < vsInfo.length; ++i) { + for (let i = 0; i < vsInfo.length; ++i) { const info = vsInfo[i] - this.addLog(`checking VS${info.versionYear} (${info.version}) found ` + - `at:\n"${info.path}"`) + this.addLog( + `checking VS${info.versionYear} (${info.version}) found at:\n"${info.path}"`) if (info.msBuild) { this.addLog('- found "Visual Studio C++ core features"') @@ -228,23 +222,21 @@ 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) - }, + this.addLog('could not find a version of Visual Studio 2017 or newer to use') + } // Helper - process version information - getVersionInfo: function getVersionInfo (info) { + getVersionInfo (info) { const match = /^(\d+)\.(\d+)\..*/.exec(info.version) if (!match) { this.log.silly('- failed to parse version:', info.version) return {} } this.log.silly('- version match = %j', match) - var ret = { + const ret = { version: info.version, versionMajor: parseInt(match[1], 10), versionMinor: parseInt(match[2], 10) @@ -259,25 +251,25 @@ VisualStudioFinder.prototype = { } this.log.silly('- unsupported version:', ret.versionMajor) return {} - }, + } // Helper - process MSBuild information - getMSBuild: function getMSBuild (info, versionYear) { + getMSBuild (info, versionYear) { const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' if (info.packages.indexOf(pkg) !== -1) { this.log.silly('- found VC.MSBuild.Base') if (versionYear === 2017) { - return path.join(info.path, 'MSBuild', '15.0', 'Bin', 'MSBuild.exe') + return path.join(info.path, 'MSBuild\\15.0\\Bin\\MSBuild.exe') } if (versionYear === 2019) { - return path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') + return path.join(info.path, 'MSBuild\\Current\\Bin\\MSBuild.exe') } } return null - }, + } // Helper - process toolset information - getToolset: function getToolset (info, versionYear) { + getToolset (info, versionYear) { const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' const express = 'Microsoft.VisualStudio.WDExpress' @@ -296,14 +288,14 @@ VisualStudioFinder.prototype = { } this.log.silly('- invalid versionYear:', versionYear) return null - }, + } // Helper - process Windows SDK information - getSDK: function getSDK (info) { + getSDK (info) { const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' - var Win10SDKVer = 0 + let Win10SDKVer = 0 info.packages.forEach((pkg) => { if (!pkg.startsWith(win10SDKPrefix)) { return @@ -330,25 +322,25 @@ VisualStudioFinder.prototype = { return '8.1' } return null - }, + } // Find an installation of Visual Studio 2015 to use - findVisualStudio2015: function findVisualStudio2015 (cb) { + async findVisualStudio2015 () { return this.findOldVS({ version: '14.0', versionMajor: 14, versionMinor: 0, versionYear: 2015, toolset: 'v140' - }, cb) - }, + }) + } // Find an installation of Visual Studio 2013 to use - findVisualStudio2013: function findVisualStudio2013 (cb) { + async 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', @@ -356,55 +348,55 @@ VisualStudioFinder.prototype = { versionMinor: 0, versionYear: 2013, toolset: 'v120' - }, cb) - }, + }) + } // Helper - common code for VS2013 and VS2015 - findOldVS: function findOldVS (info, cb) { + async 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) - } + let res + try { + res = await this.regSearchKeys(regVC7, info.version, []) + } catch (err) { + this.addLog('- not found') + return null + } - 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) - }) - }) - }, + const vsPath = path.resolve(res, '..') + this.addLog(`- found in "${vsPath}"`) + + const msBuildRegOpts = process.arch === 'ia32' ? [] : ['/reg:32'] + 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 + } // After finding a usable version of Visual Stuido: // - add it to validVersions to be displayed at the end if a specific // version was requested and not found; // - check if this is the version that was requested. // - check if this matches the Visual Studio Command Prompt - checkConfigVersion: function checkConfigVersion (versionYear, vsPath) { + checkConfigVersion (versionYear, vsPath) { this.validVersions.push(versionYear) this.validVersions.push(vsPath) @@ -412,13 +404,11 @@ VisualStudioFinder.prototype = { this.addLog('- msvs_version does not match this version') return false } - if (this.configPath && - path.relative(this.configPath, vsPath) !== '') { + if (this.configPath && path.relative(this.configPath, vsPath) !== '') { this.addLog('- msvs_version does not point to this installation') return false } - if (this.envVcInstallDir && - path.relative(this.envVcInstallDir, vsPath) !== '') { + if (this.envVcInstallDir && path.relative(this.envVcInstallDir, vsPath) !== '') { this.addLog('- does not match this Visual Studio Command Prompt') return false } diff --git a/lib/node-gyp.js b/lib/node-gyp.js index 81fc590919..95b5716448 100644 --- a/lib/node-gyp.js +++ b/lib/node-gyp.js @@ -1,210 +1,80 @@ 'use strict' const path = require('path') -const nopt = require('nopt') const log = require('npmlog') const childProcess = require('child_process') const EE = require('events').EventEmitter const inherits = require('util').inherits -const commands = [ - // Module build commands - 'build', - 'clean', - 'configure', - 'rebuild', - // Development Header File management commands - 'install', - 'list', - 'remove' -] -const aliases = { - ls: 'list', - rm: 'remove' -} +const pkg = require('../package.json') +const args = require('./args') // differentiate node-gyp's logs from npm's log.heading = 'gyp' -function gyp () { - return new Gyp() -} - -function Gyp () { - var 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) +class Gyp { + constructor () { + this.devDir = '' + this.package = pkg + this.configDefs = args.configDefs + this.shorthands = args.shorthands + this.aliases = args.aliases + + this.commands = {} + for (const command of args.commands) { + this.commands[command] = (argv, callback) => { + log.verbose('command', command, argv) + return require(`./${command}`)(this, argv, callback) + } } - }) -} -inherits(Gyp, EE) -exports.Gyp = Gyp -var proto = Gyp.prototype - -/** - * Export the contents of the package.json. - */ - -proto.package = require('../package.json') - -/** - * nopt configuration definitions - */ - -proto.configDefs = { - help: Boolean, // everywhere - arch: String, // 'configure' - cafile: String, // 'install' - debug: Boolean, // 'build' - directory: String, // bin - make: String, // 'build' - msvs_version: String, // 'configure' - ensure: Boolean, // 'install' - solution: String, // 'build' (windows only) - proxy: String, // 'install' - noproxy: String, // 'install' - devdir: String, // everywhere - nodedir: String, // 'configure' - loglevel: String, // everywhere - python: String, // 'configure' - 'dist-url': String, // 'install' - tarball: String, // 'install' - jobs: String, // 'build' - thin: String // 'configure' -} - -/** - * nopt shorthands - */ - -proto.shorthands = { - release: '--no-debug', - C: '--directory', - debug: '--debug', - j: '--jobs', - silly: '--loglevel=silly', - verbose: '--loglevel=verbose', - silent: '--loglevel=silent' -} - -/** - * expose the command aliases for the bin file to use. - */ - -proto.aliases = aliases - -/** - * Parses the given argv array and sets the 'opts', - * 'argv' and 'command' properties. - */ - -proto.parseArgv = function parseOpts (argv) { - this.opts = nopt(this.configDefs, this.shorthands, argv) - this.argv = this.opts.argv.remain.slice() - - var commands = this.todo = [] + } - // create a copy of the argv array with aliases mapped - argv = this.argv.map(function (arg) { - // is this an alias? - if (arg in this.aliases) { - arg = this.aliases[arg] - } - return arg - }, this) + /** + * Version number getter. + */ + get version () { + return this.package.version + } - // process the mapped args into "command" objects ("name" and "args" props) - argv.slice().forEach(function (arg) { - if (arg in this.commands) { - var args = argv.splice(0, argv.indexOf(arg)) - argv.shift() - if (commands.length > 0) { - commands[commands.length - 1].args = args - } - commands.push({ name: arg, args: [] }) - } - }, this) - if (commands.length > 0) { - commands[commands.length - 1].args = argv.splice(0) + parseArgv (_argv) { + const { opts, argv, todo } = args(_argv) + this.opts = opts + this.argv = argv + this.todo = todo } - // support for inheriting config env variables from npm - var npmConfigPrefix = 'npm_config_' - Object.keys(process.env).forEach(function (name) { - if (name.indexOf(npmConfigPrefix) !== 0) { - return + /** + * Spawns a child process and emits a 'spawn' event. + */ + spawn (command, args, opts) { + if (!opts) { + opts = {} } - var val = process.env[name] - if (name === npmConfigPrefix + 'loglevel') { - log.level = val - } else { - // add the user-defined options to the config - name = name.substring(npmConfigPrefix.length) - // gyp@741b7f1 enters an infinite loop when it encounters - // zero-length options so ensure those don't get through. - if (name) { - this.opts[name] = val - } + + if (!opts.silent && !opts.stdio) { + opts.stdio = [0, 1, 2] } - }, this) - if (this.opts.loglevel) { - log.level = this.opts.loglevel + const cp = childProcess.spawn(command, args, opts) + log.info('spawn', command) + log.info('spawn args', args) + return cp } - log.resume() -} - -/** - * Spawns a child process and emits a 'spawn' event. - */ -proto.spawn = function spawn (command, args, opts) { - if (!opts) { - opts = {} - } - if (!opts.silent && !opts.stdio) { - opts.stdio = [0, 1, 2] - } - var cp = childProcess.spawn(command, args, opts) - log.info('spawn', command) - log.info('spawn args', args) - return cp -} + /** + * Returns the usage instructions for node-gyp. + */ + usage () { + return ` + Usage: node-gyp [options] -/** - * Returns the usage instructions for node-gyp. - */ + where is one of: +${args.commands.map((c) => ` - ${c} - ${require(`./${c}`).usage}`).join('\n')} -proto.usage = function usage () { - var str = [ - '', - ' Usage: node-gyp [options]', - '', - ' where is one of:', - commands.map(function (c) { - return ' - ' + c + ' - ' + require('./' + c).usage - }).join('\n'), - '', - 'node-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'), - 'node@' + process.versions.node - ].join('\n') - return str +node-gyp@${this.version} ${path.resolve(__dirname, '..')} +node@${process.versions.node}` + } } -/** - * Version number getter. - */ - -Object.defineProperty(proto, 'version', { - get: function () { - return this.package.version - }, - enumerable: true -}) +inherits(Gyp, EE) -module.exports = exports = gyp +module.exports = Gyp diff --git a/lib/util.js b/lib/util.js index 3e23c628e6..53e011292c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -17,44 +17,49 @@ function logWithPrefix (log, prefix) { } } -function regGetValue (key, value, addOpts, cb) { +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 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 stdout = await new Promise((resolve, reject) => { + const child = execFile(reg, regArgs, { encoding: 'utf8' }, (err, stdout, stderr) => { + if (err) { + 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 reject(err) + } } - - 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]) + resolve(stdout) }) - child.stdin.end() + child.stdin.end() + }) + + 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) { - var i = 0 - const search = () => { +async function regSearchKeys (keys, value, addOpts) { + async function search (i) { 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) + if (err && i < keys.length - 1) { + return search(i + 1) + } + return res }) } - search() + return search(0) } module.exports = { diff --git a/package.json b/package.json index 478f43cb81..0431f53270 100644 --- a/package.json +++ b/package.json @@ -24,25 +24,26 @@ "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", - "graceful-fs": "^4.2.2", + "graceful-fs": "^4.2.3", "mkdirp": "^0.5.1", - "nopt": "^4.0.1", + "nopt": "^4.0.3", "npmlog": "^4.1.2", - "request": "^2.88.0", + "request": "^2.88.2", "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" + "semver": "^7.3.2", + "tar": "^6.0.1", + "which": "^2.0.2" }, "engines": { "node": ">= 6.0.0" }, "devDependencies": { "bindings": "^1.5.0", - "nan": "^2.14.0", + "bl": "^4.0.2", + "nan": "^2.14.1", "require-inject": "^1.4.4", - "standard": "^14.3.1", - "tap": "~12.7.0" + "standard": "^14.3.4", + "tap": "^14.10.7" }, "scripts": { "lint": "standard */*.js test/**/*.js", diff --git a/test/node_modules/hello_world/hello.js b/test/node_modules/hello_world/hello.js index 2fd10718db..93211d5daf 100644 --- a/test/node_modules/hello_world/hello.js +++ b/test/node_modules/hello_world/hello.js @@ -1,3 +1,3 @@ 'use strict' -var addon = require('bindings')('hello'); -exports.hello = function() { return addon.hello() } +const addon = require('bindings')('hello') +exports.hello = () => addon.hello() diff --git a/test/process-exec-sync.js b/test/process-exec-sync.js deleted file mode 100644 index 21763bc26d..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) { - var child, error, timeout, tmpdir, 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 - timeout = Date.now() + options.timeout - // init tmpdir - var osTempBase = '/tmp' - var os = determineOS() - osTempBase = '/tmp' - - if (process.env.TMP) { - osTempBase = process.env.TMP - } - - if (osTempBase[osTempBase.length - 1] !== '/') { - osTempBase += '/' - } - - 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 - child = childProcess.exec(command, options) - - var maxTry = 100000 // increases the test time by 6 seconds on win-2016-node-0.10 - var tryCount = 0 - while (tryCount < maxTry) { - try { - var 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) { - var command, quote - command = file - if (args.length > 0) { - for (var 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 () { - var os = '' - var 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/simple-proxy.js b/test/simple-proxy.js index cb0dfcfec7..749208f75b 100644 --- a/test/simple-proxy.js +++ b/test/simple-proxy.js @@ -2,26 +2,24 @@ const http = require('http') const https = require('https') -const server = http.createServer(handler) -const port = +process.argv[2] + +const port = parseInt(process.argv[2], 10) const prefix = process.argv[3] const upstream = process.argv[4] -var calls = 0 - -server.listen(port) +let calls = 0 -function handler (req, res) { +const server = http.createServer((req, res) => { if (req.url.indexOf(prefix) !== 0) { - throw new Error('request url [' + req.url + '] does not start with [' + prefix + ']') + throw new Error(`request url [${req.url}] does not start with [${prefix}]`) } - var upstreamUrl = upstream + req.url.substring(prefix.length) - https.get(upstreamUrl, function (ures) { - ures.on('end', function () { + const upstreamUrl = `${upstream}${req.url.substring(prefix.length)}` + https.get(upstreamUrl, (ures) => { + ures.on('end', () => { if (++calls === 2) { server.close() } }) ures.pipe(res) }) -} +}).listen(port) diff --git a/test/tap-parallel-not-ok b/test/tap-parallel-not-ok new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/test-addon.js b/test/test-addon.js index f79eff73c1..64bdcce4c2 100644 --- a/test/test-addon.js +++ b/test/test-addon.js @@ -1,48 +1,47 @@ 'use strict' -const test = require('tap').test +const { test } = require('tap') const path = require('path') -const fs = require('graceful-fs') -const childProcess = require('child_process') +const fs = require('fs') +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 + +const addonPath = path.resolve(__dirname, 'node_modules/hello_world') +const nodeGyp = path.resolve(__dirname, '../bin/node-gyp.js') function runHello (hostProcess) { if (!hostProcess) { hostProcess = process.execPath } - var testCode = "console.log(require('hello_world').hello())" + const testCode = 'console.log(require(\'hello_world\').hello())' return execFileSync(hostProcess, ['-e', testCode], { cwd: __dirname }).toString() } function getEncoding () { - var code = 'import locale;print(locale.getdefaultlocale()[1])' + const code = 'import locale;print(locale.getdefaultlocale()[1])' return execFileSync('python', ['-c', code]).toString().trim() } function checkCharmapValid () { - var data + let data try { data = execFileSync('python', ['fixtures/test-charmap.py'], { cwd: __dirname }) } catch (err) { return false } - var lines = data.toString().trim().split('\n') + const lines = data.toString().trim().split('\n') return lines.pop() === 'True' } -test('build simple addon', function (t) { +test('build simple addon', (t) => { t.plan(3) // Set the loglevel otherwise the output disappears when run via 'npm test' - var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose'] - var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) { - var logLines = stderr.toString().trim().split(/\r?\n/) - var lastLine = logLines[logLines.length - 1] + const cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose'] + const proc = execFile(process.execPath, cmd, (err, stdout, stderr) => { + const logLines = stderr.toString().trim().split(/\r?\n/) + const lastLine = logLines[logLines.length - 1] t.strictEqual(err, null) t.strictEqual(lastLine, 'gyp info ok', 'should end in ok') t.strictEqual(runHello().trim(), 'world') @@ -51,20 +50,20 @@ test('build simple addon', function (t) { proc.stderr.setEncoding('utf-8') }) -test('build simple addon in path with non-ascii characters', function (t) { +test('build simple addon in path with non-ascii characters', (t) => { t.plan(1) if (!checkCharmapValid()) { return t.skip('python console app can\'t encode non-ascii character.') } - var testDirNames = { + const testDirNames = { cp936: '文件夹', cp1252: 'Latīna', cp932: 'フォルダ' } // Select non-ascii characters by current encoding - var testDirName = testDirNames[getEncoding()] + const testDirName = testDirNames[getEncoding()] // If encoding is UTF-8 or other then no need to test if (!testDirName) { return t.skip('no need to test') @@ -72,17 +71,17 @@ test('build simple addon in path with non-ascii characters', function (t) { t.plan(3) - var data - var configPath = path.join(addonPath, 'build', 'config.gypi') + let data + const configPath = path.join(addonPath, 'build', 'config.gypi') try { data = fs.readFileSync(configPath, 'utf8') } catch (err) { t.error(err) return } - var config = JSON.parse(data.replace(/#.+\n/, '')) - var nodeDir = config.variables.nodedir - var testNodeDir = path.join(addonPath, testDirName) + const config = JSON.parse(data.replace(/#.+\n/, '')) + const nodeDir = config.variables.nodedir + const testNodeDir = path.join(addonPath, testDirName) // Create symbol link to path with non-ascii characters try { fs.symlinkSync(nodeDir, testNodeDir, 'dir') @@ -98,7 +97,7 @@ test('build simple addon in path with non-ascii characters', function (t) { } } - var cmd = [ + const cmd = [ nodeGyp, 'rebuild', '-C', @@ -106,15 +105,15 @@ test('build simple addon in path with non-ascii characters', function (t) { '--loglevel=verbose', '-nodedir=' + testNodeDir ] - var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) { + const proc = execFile(process.execPath, cmd, (err, stdout, stderr) => { try { - fs.unlink(testNodeDir) + fs.unlinkSync(testNodeDir) } catch (err) { t.error(err) } - var logLines = stderr.toString().trim().split(/\r?\n/) - var lastLine = logLines[logLines.length - 1] + const logLines = stderr.toString().trim().split(/\r?\n/) + const lastLine = logLines[logLines.length - 1] t.strictEqual(err, null) t.strictEqual(lastLine, 'gyp info ok', 'should end in ok') t.strictEqual(runHello().trim(), 'world') @@ -123,23 +122,16 @@ test('build simple addon in path with non-ascii characters', function (t) { proc.stderr.setEncoding('utf-8') }) -test('addon works with renamed host executable', function (t) { - // No `fs.copyFileSync` before node8. - if (process.version.substr(1).split('.')[0] < 8) { - t.skip('skipping test for old node version') - t.end() - return - } - +test('addon works with renamed host executable', (t) => { t.plan(3) - var notNodePath = path.join(os.tmpdir(), 'notnode' + path.extname(process.execPath)) + const notNodePath = path.join(os.tmpdir(), `notnode${path.extname(process.execPath)}`) fs.copyFileSync(process.execPath, notNodePath) - var cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose'] - var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) { - var logLines = stderr.toString().trim().split(/\r?\n/) - var lastLine = logLines[logLines.length - 1] + const cmd = [nodeGyp, 'rebuild', '-C', addonPath, '--loglevel=verbose'] + const proc = execFile(process.execPath, cmd, (err, stdout, stderr) => { + const logLines = stderr.toString().trim().split(/\r?\n/) + const lastLine = logLines[logLines.length - 1] t.strictEqual(err, null) t.strictEqual(lastLine, 'gyp info ok', 'should end in ok') t.strictEqual(runHello(notNodePath).trim(), 'world') diff --git a/test/test-configure-python.js b/test/test-configure-python.js index d08f9e5ed3..05f2e9252b 100644 --- a/test/test-configure-python.js +++ b/test/test-configure-python.js @@ -1,78 +1,79 @@ 'use strict' -const test = require('tap').test +const { test } = require('tap') const path = require('path') const devDir = require('./common').devDir() -const gyp = require('../lib/node-gyp') +const Gyp = require('../lib/node-gyp') 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, {}) } + openSync: () => 0, + closeSync: () => {}, + writeFile: (file, data, cb) => cb(), + stat: (file, cb) => cb(null, {}) } }) -const EXPECTED_PYPATH = path.join(__dirname, '..', 'gyp', 'pylib') +const EXPECTED_PYPATH = path.join(__dirname, '../gyp/pylib') const SEPARATOR = process.platform === 'win32' ? ';' : ':' -const SPAWN_RESULT = { on: function () { } } +const SPAWN_RESULT = { on: () => {} } require('npmlog').level = 'warn' -test('configure PYTHONPATH with no existing env', function (t) { +test('configure PYTHONPATH with no existing env', (t) => { t.plan(1) delete process.env.PYTHONPATH - var prog = gyp() - prog.parseArgv([]) - prog.spawn = function () { + const gyp = new Gyp() + gyp.parseArgv([]) + gyp.spawn = () => { t.equal(process.env.PYTHONPATH, EXPECTED_PYPATH) return SPAWN_RESULT } - prog.devDir = devDir - configure(prog, [], t.fail) + gyp.devDir = devDir + configure(gyp, [], t.fail) }) -test('configure PYTHONPATH with existing env of one dir', function (t) { +test('configure PYTHONPATH with existing env of one dir', (t) => { t.plan(2) - var existingPath = path.join('a', 'b') + const existingPath = path.join('a', 'b') process.env.PYTHONPATH = existingPath - var prog = gyp() - prog.parseArgv([]) - prog.spawn = function () { + const gyp = new Gyp() + gyp.parseArgv([]) + gyp.spawn = () => { t.equal(process.env.PYTHONPATH, [EXPECTED_PYPATH, existingPath].join(SEPARATOR)) - var dirs = process.env.PYTHONPATH.split(SEPARATOR) + const dirs = process.env.PYTHONPATH.split(SEPARATOR) t.deepEqual(dirs, [EXPECTED_PYPATH, existingPath]) return SPAWN_RESULT } - prog.devDir = devDir - configure(prog, [], t.fail) + gyp.devDir = devDir + configure(gyp, [], t.fail) }) -test('configure PYTHONPATH with existing env of multiple dirs', function (t) { +test('configure PYTHONPATH with existing env of multiple dirs', (t) => { t.plan(2) - var pythonDir1 = path.join('a', 'b') - var pythonDir2 = path.join('b', 'c') - var existingPath = [pythonDir1, pythonDir2].join(SEPARATOR) + const pythonDir1 = path.join('a', 'b') + const pythonDir2 = path.join('b', 'c') + const existingPath = [pythonDir1, pythonDir2].join(SEPARATOR) process.env.PYTHONPATH = existingPath - var prog = gyp() - prog.parseArgv([]) - prog.spawn = function () { + const gyp = new Gyp() + gyp.parseArgv([]) + gyp.spawn = () => { t.equal(process.env.PYTHONPATH, [EXPECTED_PYPATH, existingPath].join(SEPARATOR)) - var dirs = process.env.PYTHONPATH.split(SEPARATOR) + const dirs = process.env.PYTHONPATH.split(SEPARATOR) t.deepEqual(dirs, [EXPECTED_PYPATH, pythonDir1, pythonDir2]) return SPAWN_RESULT } - prog.devDir = devDir - configure(prog, [], t.fail) + gyp.devDir = devDir + configure(gyp, [], t.fail) }) diff --git a/test/test-download.js b/test/test-download.js index fe373e3280..dd85caf570 100644 --- a/test/test-download.js +++ b/test/test-download.js @@ -1,204 +1,187 @@ 'use strict' -const test = require('tap').test -const fs = require('fs') +const { test } = require('tap') +const { promisify } = require('util') +const fs = require('fs').promises +// TODO: removeme +const fsOLD = require('fs') const path = require('path') const http = require('http') const https = require('https') -const install = require('../lib/install') const semver = require('semver') -const devDir = require('./common').devDir() -const rimraf = require('rimraf') -const gyp = require('../lib/node-gyp') +const rimraf = promisify(require('rimraf')) const log = require('npmlog') +const bl = require('bl') + +const install = require('../lib/install') +const devDir = require('./common').devDir() +const Gyp = require('../lib/node-gyp') log.level = 'warn' -test('download over http', function (t) { - t.plan(2) +test('download over http', (t) => { + t.plan(3) - var server = http.createServer(function (req, res) { + const server = http.createServer((req, res) => { t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') + `node-gyp v42 (node ${process.version})`) res.end('ok') server.close() }) - var host = 'localhost' - server.listen(0, host, function () { - var port = this.address().port - var gyp = { + const host = 'localhost' + server.listen(0, host, () => { + const port = server.address().port + const gyp = { opts: {}, version: '42' } - var url = 'http://' + host + ':' + port - var req = install.test.download(gyp, {}, url) - req.on('response', function (res) { - var body = '' - res.setEncoding('utf8') - res.on('data', function (data) { - body += data - }) - res.on('end', function () { - t.strictEqual(body, 'ok') - }) + const url = `http://${host}:${port}` + const req = install.test.download(gyp, {}, url) + req.on('response', (res) => { + res.pipe(bl((err, body) => { + t.error(err) + t.strictEqual(body.toString(), 'ok') + })) }) }) }) -test('download over https with custom ca', function (t) { - t.plan(3) - - var cert = fs.readFileSync(path.join(__dirname, 'fixtures/server.crt'), 'utf8') - var key = fs.readFileSync(path.join(__dirname, 'fixtures/server.key'), 'utf8') - - var cafile = path.join(__dirname, '/fixtures/ca.crt') - var ca = install.test.readCAFile(cafile) - t.strictEqual(ca.length, 1) +test('download over https with custom ca', (t) => { + let cert, key - var options = { ca: ca, cert: cert, key: key } - var server = https.createServer(options, function (req, res) { - t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') - res.end('ok') - server.close() + t.test('setup', async (t) => { + cert = await fs.readFile(path.join(__dirname, 'fixtures/server.crt'), 'utf8') + key = await fs.readFile(path.join(__dirname, 'fixtures/server.key'), 'utf8') }) - server.on('clientError', function (err) { - throw err - }) + return t.test('run', (t) => { + t.plan(4) + const cafile = path.join(__dirname, '/fixtures/ca.crt') + const ca = install.test.readCAFile(cafile) + t.strictEqual(ca.length, 1) + + const options = { ca, cert, key } + const server = https.createServer(options, (req, res) => { + t.strictEqual(req.headers['user-agent'], + `node-gyp v42 (node ${process.version})`) + res.end('ok') + server.close() + }) - var host = 'localhost' - server.listen(8000, host, function () { - var port = this.address().port - var gyp = { - opts: { cafile: cafile }, - version: '42' - } - var url = 'https://' + host + ':' + port - var req = install.test.download(gyp, {}, url) - req.on('response', function (res) { - var body = '' - res.setEncoding('utf8') - res.on('data', function (data) { - body += data - }) - res.on('end', function () { - t.strictEqual(body, 'ok') + server.on('clientError', (err) => { + throw err + }) + + const host = 'localhost' + server.listen(8000, host, () => { + const port = server.address().port + const gyp = { + opts: { cafile: cafile }, + version: '42' + } + const url = `https://${host}:${port}` + const req = install.test.download(gyp, {}, url) + req.on('response', (res) => { + res.pipe(bl((err, body) => { + t.error(err) + t.strictEqual(body.toString(), 'ok') + })) }) }) }) }) -test('download over http with proxy', function (t) { - t.plan(2) +test('download over http with proxy', (t) => { + t.plan(3) - var server = http.createServer(function (req, res) { + const server = http.createServer((req, res) => { t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') + `node-gyp v42 (node ${process.version})`) res.end('ok') - pserver.close(function () { - server.close() - }) + pserver.close(() => server.close()) }) - var pserver = http.createServer(function (req, res) { + const pserver = http.createServer((req, res) => { t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') + `node-gyp v42 (node ${process.version})`) res.end('proxy ok') - server.close(function () { - pserver.close() - }) + server.close(() => pserver.close()) }) - var host = 'localhost' - server.listen(0, host, function () { - var port = this.address().port - pserver.listen(port + 1, host, function () { - var gyp = { + const host = 'localhost' + server.listen(0, host, () => { + const port = server.address().port + pserver.listen(port + 1, host, () => { + const gyp = { opts: { - proxy: 'http://' + host + ':' + (port + 1) + proxy: `http://${host}:${port + 1}` }, version: '42' } - var url = 'http://' + host + ':' + port - var req = install.test.download(gyp, {}, url) - req.on('response', function (res) { - var body = '' - res.setEncoding('utf8') - res.on('data', function (data) { - body += data - }) - res.on('end', function () { - t.strictEqual(body, 'proxy ok') - }) + const url = `http://${host}:${port}` + const req = install.test.download(gyp, {}, url) + req.on('response', (res) => { + res.pipe(bl((err, body) => { + t.error(err) + t.strictEqual(body.toString(), 'proxy ok') + })) }) }) }) }) -test('download over http with noproxy', function (t) { - t.plan(2) +test('download over http with noproxy', (t) => { + t.plan(3) - var server = http.createServer(function (req, res) { + const server = http.createServer((req, res) => { t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') + `node-gyp v42 (node ${process.version})`) res.end('ok') - pserver.close(function () { - server.close() - }) + pserver.close(() => server.close()) }) - var pserver = http.createServer(function (req, res) { + const pserver = http.createServer((req, res) => { t.strictEqual(req.headers['user-agent'], - 'node-gyp v42 (node ' + process.version + ')') + `node-gyp v42 (node ${process.version})`) res.end('proxy ok') - server.close(function () { - pserver.close() - }) + server.close(() => pserver.close()) }) - var host = 'localhost' - server.listen(0, host, function () { - var port = this.address().port - pserver.listen(port + 1, host, function () { - var gyp = { + const host = 'localhost' + server.listen(0, host, () => { + const port = server.address().port + pserver.listen(port + 1, host, () => { + const gyp = { opts: { - proxy: 'http://' + host + ':' + (port + 1), + proxy: `http://${host}:${(port + 1)}`, noproxy: 'localhost' }, version: '42' } - var url = 'http://' + host + ':' + port - var req = install.test.download(gyp, {}, url) - req.on('response', function (res) { - var body = '' - res.setEncoding('utf8') - res.on('data', function (data) { - body += data - }) - res.on('end', function () { - t.strictEqual(body, 'ok') - }) + const url = `http://${host}:${port}` + const req = install.test.download(gyp, {}, url) + req.on('response', (res) => { + res.pipe(bl((err, body) => { + t.error(err) + t.strictEqual(body.toString(), 'ok') + })) }) }) }) }) -test('download with missing cafile', function (t) { +test('download with missing cafile', (t) => { t.plan(1) - var gyp = { + const gyp = { opts: { cafile: 'no.such.file' } } - try { - install.test.download(gyp, {}, 'http://bad/') - } catch (e) { - t.ok(/no.such.file/.test(e.message)) - } + + t.throws(() => { install.test.download(gyp, {}, 'http://bad/') }, /no.such.file/) }) -test('check certificate splitting', function (t) { - var cas = install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt')) +test('check certificate splitting', (t) => { + const cas = install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt')) t.plan(2) t.strictEqual(cas.length, 2) t.notStrictEqual(cas[0], cas[1]) @@ -206,7 +189,7 @@ test('check certificate splitting', function (t) { // only run this test if we are running a version of Node with predictable version path behavior -test('download headers (actual)', function (t) { +test('download headers (actual)', async (t) => { if (process.env.FAST_TEST || process.release.name !== 'node' || semver.prerelease(process.version) !== null || @@ -214,26 +197,27 @@ test('download headers (actual)', function (t) { return t.skip('Skipping actual download of headers due to test environment configuration') } - t.plan(17) + t.plan(16) const expectedDir = path.join(devDir, process.version.replace(/^v/, '')) - rimraf(expectedDir, (err) => { - t.ifError(err) - - const prog = gyp() - prog.parseArgv([]) - prog.devDir = devDir - log.level = 'warn' - install(prog, [], (err) => { - t.ifError(err) - - fs.readFile(path.join(expectedDir, 'installVersion'), 'utf8', (err, data) => { - t.ifError(err) + await rimraf(expectedDir) + + const gyp = new Gyp() + gyp.parseArgv([]) + gyp.devDir = devDir + log.level = 'warn' + + return new Promise((resolve, reject) => { // TODO: removeme + install(gyp, [], (err) => { + t.error(err) + + fsOLD.readFile(path.join(expectedDir, 'installVersion'), 'utf8', (err, data) => { + t.error(err) t.strictEqual(data, '9\n', 'correct installVersion') }) - fs.readdir(path.join(expectedDir, 'include/node'), (err, list) => { - t.ifError(err) + fsOLD.readdir(path.join(expectedDir, 'include/node'), (err, list) => { + t.error(err) t.ok(list.includes('common.gypi')) t.ok(list.includes('config.gypi')) @@ -247,8 +231,8 @@ test('download headers (actual)', function (t) { t.ok(list.includes('zlib.h')) }) - fs.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8', (err, contents) => { - t.ifError(err) + fsOLD.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8', (err, contents) => { + t.error(err) const lines = contents.split('\n') @@ -262,6 +246,7 @@ test('download headers (actual)', function (t) { }, '') t.strictEqual(version, process.version) + resolve() }) }) }) diff --git a/test/test-find-accessible-sync.js b/test/test-find-accessible-sync.js index 0a2e584c4f..ace5a71839 100644 --- a/test/test-find-accessible-sync.js +++ b/test/test-find-accessible-sync.js @@ -1,18 +1,16 @@ 'use strict' -const test = require('tap').test +const { test } = require('tap') const path = require('path') const requireInject = require('require-inject') const configure = requireInject('../lib/configure', { 'graceful-fs': { - closeSync: function () { return undefined }, - openSync: function (path) { - if (readableFiles.some(function (f) { return f === path })) { + closeSync: () => {}, + openSync: (path) => { + if (readableFiles.some((f) => f === path)) { return 0 - } else { - var error = new Error('ENOENT - not found') - throw error } + throw new Error('ENOENT - not found') } } }) @@ -27,58 +25,58 @@ const readableFiles = [ path.resolve(dir, readableFileInDir) ] -test('find accessible - empty array', function (t) { +test('find accessible - empty array', (t) => { t.plan(1) - var candidates = [] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = [] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, undefined) }) -test('find accessible - single item array, readable', function (t) { +test('find accessible - single item array, readable', (t) => { t.plan(1) - var candidates = [readableFile] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = [readableFile] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, path.resolve(dir, readableFile)) }) -test('find accessible - single item array, readable in subdir', function (t) { +test('find accessible - single item array, readable in subdir', (t) => { t.plan(1) - var candidates = [readableFileInDir] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = [readableFileInDir] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, path.resolve(dir, readableFileInDir)) }) -test('find accessible - single item array, unreadable', function (t) { +test('find accessible - single item array, unreadable', (t) => { t.plan(1) - var candidates = ['unreadable_file'] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = ['unreadable_file'] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, undefined) }) -test('find accessible - multi item array, no matches', function (t) { +test('find accessible - multi item array, no matches', (t) => { t.plan(1) - var candidates = ['non_existent_file', 'unreadable_file'] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = ['non_existent_file', 'unreadable_file'] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, undefined) }) -test('find accessible - multi item array, single match', function (t) { +test('find accessible - multi item array, single match', (t) => { t.plan(1) - var candidates = ['non_existent_file', readableFile] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = ['non_existent_file', readableFile] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, path.resolve(dir, readableFile)) }) -test('find accessible - multi item array, return first match', function (t) { +test('find accessible - multi item array, return first match', (t) => { t.plan(1) - var candidates = ['non_existent_file', anotherReadableFile, readableFile] - var found = configure.test.findAccessibleSync('test', dir, candidates) + const candidates = ['non_existent_file', anotherReadableFile, readableFile] + const found = configure.test.findAccessibleSync('test', dir, candidates) t.strictEqual(found, path.resolve(dir, anotherReadableFile)) }) diff --git a/test/test-find-node-directory.js b/test/test-find-node-directory.js index f1380d162a..03680c2a51 100644 --- a/test/test-find-node-directory.js +++ b/test/test-find-node-directory.js @@ -1,6 +1,6 @@ 'use strict' -const test = require('tap').test +const { test } = require('tap') const path = require('path') const findNodeDirectory = require('../lib/find-node-directory') @@ -10,10 +10,10 @@ const platforms = ['darwin', 'freebsd', 'linux', 'sunos', 'win32', 'aix'] // the script is running in and it should match the layout // in a build tree where npm is installed in // .... /deps/npm -test('test find-node-directory - node install', function (t) { +test('test find-node-directory - node install', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj = { execPath: '/x/y/bin/node', platform: platforms[next] } + for (const platform in platforms) { + const processObj = { execPath: '/x/y/bin/node', platform } t.equal( findNodeDirectory('/x/deps/npm/node_modules/node-gyp/lib', processObj), path.join('/x')) @@ -25,11 +25,11 @@ test('test find-node-directory - node install', function (t) { // in an installed tree where npm is installed in // .... /lib/node_modules/npm or .../node_modules/npm // depending on the patform -test('test find-node-directory - node build', function (t) { +test('test find-node-directory - node build', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj = { execPath: '/x/y/bin/node', platform: platforms[next] } - if (platforms[next] === 'win32') { + for (const platform in platforms) { + const processObj = { execPath: '/x/y/bin/node', platform } + if (platform === 'win32') { t.equal( findNodeDirectory('/y/node_modules/npm/node_modules/node-gyp/lib', processObj), path.join('/y')) @@ -43,10 +43,10 @@ test('test find-node-directory - node build', function (t) { // we should find the directory based on the execPath // for node and match because it was in the bin directory -test('test find-node-directory - node in bin directory', function (t) { +test('test find-node-directory - node in bin directory', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj = { execPath: '/x/y/bin/node', platform: platforms[next] } + for (const platform in platforms) { + const processObj = { execPath: '/x/y/bin/node', platform } t.equal( findNodeDirectory('/nothere/npm/node_modules/node-gyp/lib', processObj), path.join('/x/y')) @@ -55,17 +55,14 @@ test('test find-node-directory - node in bin directory', function (t) { // we should find the directory based on the execPath // for node and match because it was in the Release directory -test('test find-node-directory - node in build release dir', function (t) { +test('test find-node-directory - node in build release dir', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj - if (platforms[next] === 'win32') { - processObj = { execPath: '/x/y/Release/node', platform: platforms[next] } + for (const platform in platforms) { + let processObj + if (platform === 'win32') { + processObj = { execPath: '/x/y/Release/node', platform } } else { - processObj = { - execPath: '/x/y/out/Release/node', - platform: platforms[next] - } + processObj = { execPath: '/x/y/out/Release/node', platform } } t.equal( @@ -76,14 +73,14 @@ test('test find-node-directory - node in build release dir', function (t) { // we should find the directory based on the execPath // for node and match because it was in the Debug directory -test('test find-node-directory - node in Debug release dir', function (t) { +test('test find-node-directory - node in Debug release dir', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj - if (platforms[next] === 'win32') { - processObj = { execPath: '/a/b/Debug/node', platform: platforms[next] } + for (const platform in platforms) { + let processObj + if (platform === 'win32') { + processObj = { execPath: '/a/b/Debug/node', platform } } else { - processObj = { execPath: '/a/b/out/Debug/node', platform: platforms[next] } + processObj = { execPath: '/a/b/out/Debug/node', platform } } t.equal( @@ -94,10 +91,10 @@ test('test find-node-directory - node in Debug release dir', function (t) { // we should not find it as it will not match based on the execPath nor // the directory from which the script is running -test('test find-node-directory - not found', function (t) { +test('test find-node-directory - not found', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj = { execPath: '/x/y/z/y', platform: next } + for (const platform in platforms) { + const processObj = { execPath: '/x/y/z/y', platform } t.equal(findNodeDirectory('/a/b/c/d', processObj), '') } }) @@ -108,10 +105,10 @@ test('test find-node-directory - not found', function (t) { // .... /deps/npm // same test as above but make sure additional directory entries // don't cause an issue -test('test find-node-directory - node install', function (t) { +test('test find-node-directory - node install', (t) => { t.plan(platforms.length) - for (var next = 0; next < platforms.length; next++) { - var processObj = { execPath: '/x/y/bin/node', platform: platforms[next] } + for (const platform in platforms) { + const processObj = { execPath: '/x/y/bin/node', platform } t.equal( findNodeDirectory('/x/y/z/a/b/c/deps/npm/node_modules/node-gyp/lib', processObj), path.join('/x/y/z/a/b/c')) diff --git a/test/test-find-python.js b/test/test-find-python.js index 6ca522a04c..d86a2dfe72 100644 --- a/test/test-find-python.js +++ b/test/test-find-python.js @@ -2,31 +2,33 @@ delete process.env.PYTHON -const test = require('tap').test -const findPython = require('../lib/find-python') -const execFile = require('child_process').execFile -const PythonFinder = findPython.test.PythonFinder +const { test } = require('tap') +const { execFile } = require('child_process') +const { findPython, PythonFinder } = require('../lib/find-python').test require('npmlog').level = 'warn' -test('find python', function (t) { - t.plan(4) +test('find python', async (t) => { + t.plan(2) - findPython.test.findPython(null, function (err, found) { - t.strictEqual(err, null) - var proc = execFile(found, ['-V'], function (err, stdout, stderr) { - t.strictEqual(err, null) - if (/Python 2/.test(stderr)) { - t.strictEqual(stdout, '') - t.ok(/Python 2/.test(stderr)) - } else { - t.ok(/Python 3/.test(stdout)) - t.strictEqual(stderr, '') + const found = await findPython(null) + const { stdout, stderr } = await new Promise((resolve, reject) => { + const proc = execFile(found, ['-V'], (err, stdout, stderr) => { + if (err) { + return reject(err) } + resolve({ stdout, stderr }) }) proc.stdout.setEncoding('utf-8') proc.stderr.setEncoding('utf-8') }) + if (/Python 2/.test(stderr)) { + t.strictEqual(stdout, '') + t.ok(/Python 2/.test(stderr)) + } else { + t.ok(/Python 3/.test(stdout)) + t.strictEqual(stderr, '') + } }) function poison (object, property) { @@ -34,7 +36,7 @@ function poison (object, property) { console.error(Error(`Property ${property} should not have been accessed.`)) process.abort() } - var descriptor = { + const descriptor = { configurable: false, enumerable: false, get: fail, @@ -43,49 +45,50 @@ function poison (object, property) { Object.defineProperty(object, property, descriptor) } -function TestPythonFinder () { - PythonFinder.apply(this, arguments) -} -TestPythonFinder.prototype = Object.create(PythonFinder.prototype) -// Silence npmlog - remove for debugging -TestPythonFinder.prototype.log = { - silly: () => {}, - verbose: () => {}, - info: () => {}, - warn: () => {}, - error: () => {} +class TestPythonFinder extends PythonFinder { + constructor (...args) { + super(...args) + + // Silence npmlog - remove for debugging + this.log = { + silly: () => {}, + verbose: () => {}, + info: () => {}, + warn: () => {}, + error: () => {} + } + + delete this.env.NODE_GYP_FORCE_PYTHON + } } -delete TestPythonFinder.prototype.env.NODE_GYP_FORCE_PYTHON -test('find python - python', function (t) { - t.plan(6) +test('find python - python', async (t) => { + t.plan(5) - var f = new TestPythonFinder('python', done) - f.execFile = function (program, args, opts, cb) { - f.execFile = function (program, args, opts, cb) { + const f = new TestPythonFinder('python') + + f.execFile = (program, args, opts, cb) => { + f.execFile = (program, args, opts, cb) => { poison(f, 'execFile') t.strictEqual(program, '/path/python') t.ok(/sys\.version_info/.test(args[1])) cb(null, '2.7.15') } - t.strictEqual(program, - process.platform === 'win32' ? '"python"' : 'python') + t.strictEqual(program, process.platform === 'win32' ? '"python"' : 'python') t.ok(/sys\.executable/.test(args[1])) cb(null, '/path/python') } - f.findPython() - function done (err, python) { - t.strictEqual(err, null) - t.strictEqual(python, '/path/python') - } + const python = await f.findPython() + t.strictEqual(python, '/path/python') }) -test('find python - python too old', function (t) { +test('find python - python too old', async (t) => { t.plan(2) - var f = new TestPythonFinder(null, done) - f.execFile = function (program, args, opts, cb) { + const f = new TestPythonFinder(null) + + f.execFile = (program, args, opts, cb) => { if (/sys\.executable/.test(args[args.length - 1])) { cb(null, '/path/python') } else if (/sys\.version_info/.test(args[args.length - 1])) { @@ -94,19 +97,17 @@ test('find python - python too old', function (t) { t.fail() } } - f.findPython() - function done (err) { - t.ok(/Could not find any Python/.test(err)) - t.ok(/not supported/i.test(f.errorLog)) - } + await t.rejects(() => f.findPython(), /Could not find any Python/) + t.ok(/not supported/i.test(f.errorLog)) }) -test('find python - no python', function (t) { +test('find python - no python', async (t) => { t.plan(2) - var f = new TestPythonFinder(null, done) - f.execFile = function (program, args, opts, cb) { + const f = new TestPythonFinder(null) + + f.execFile = (program, args, opts, cb) => { if (/sys\.executable/.test(args[args.length - 1])) { cb(new Error('not found')) } else if (/sys\.version_info/.test(args[args.length - 1])) { @@ -115,43 +116,37 @@ test('find python - no python', function (t) { t.fail() } } - f.findPython() - function done (err) { - t.ok(/Could not find any Python/.test(err)) - t.ok(/not in PATH/.test(f.errorLog)) - } + await t.rejects(() => f.findPython(), /Could not find any Python/) + t.ok(/not in PATH/.test(f.errorLog)) }) -test('find python - no python2, no python, unix', function (t) { +test('find python - no python2, no python, unix', async (t) => { t.plan(2) - var f = new TestPythonFinder(null, done) + const f = new TestPythonFinder(null) f.checkPyLauncher = t.fail f.win = false - f.execFile = function (program, args, opts, cb) { + f.execFile = (program, args, opts, cb) => { if (/sys\.executable/.test(args[args.length - 1])) { cb(new Error('not found')) } else { t.fail() } } - f.findPython() - function done (err) { - t.ok(/Could not find any Python/.test(err)) - t.ok(/not in PATH/.test(f.errorLog)) - } + await t.rejects(() => f.findPython(), /Could not find any Python/) + t.ok(/not in PATH/.test(f.errorLog)) }) -test('find python - no python, use python launcher', function (t) { - t.plan(4) +test('find python - no python, use python launcher', async (t) => { + t.plan(3) - var f = new TestPythonFinder(null, done) + const f = new TestPythonFinder(null) f.win = true - f.execFile = function (program, args, opts, cb) { + f.execFile = (program, args, opts, cb) => { if (program === 'py.exe') { t.notEqual(args.indexOf('-2'), -1) t.notEqual(args.indexOf('-c'), -1) @@ -171,49 +166,43 @@ test('find python - no python, use python launcher', function (t) { t.fail() } } - f.findPython() - function done (err, python) { - t.strictEqual(err, null) - t.strictEqual(python, 'Z:\\snake.exe') - } + const python = await f.findPython() + t.strictEqual(python, 'Z:\\snake.exe') }) -test('find python - no python, no python launcher, good guess', function (t) { - t.plan(2) +test('find python - no python, no python launcher, good guess', async (t) => { + t.plan(1) - var re = /C:[\\/]Python37[\\/]python[.]exe/ - var f = new TestPythonFinder(null, done) + const re = /C:[\\/]Python37[\\/]python[.]exe/ + const f = new TestPythonFinder(null) f.win = true - f.execFile = function (program, args, opts, cb) { + f.execFile = (program, args, opts, cb) => { if (program === 'py.exe') { return cb(new Error('not found')) } if (/sys\.executable/.test(args[args.length - 1])) { cb(new Error('not found')) } else if (re.test(program) && - /sys\.version_info/.test(args[args.length - 1])) { + /sys\.version_info/.test(args[args.length - 1])) { cb(null, '3.7.3') } else { t.fail() } } - f.findPython() - function done (err, python) { - t.strictEqual(err, null) - t.ok(re.test(python)) - } + const python = await f.findPython() + t.ok(re.test(python)) }) -test('find python - no python, no python launcher, bad guess', function (t) { +test('find python - no python, no python launcher, bad guess', async (t) => { t.plan(2) - var f = new TestPythonFinder(null, done) + const f = new TestPythonFinder(null) f.win = true - f.execFile = function (program, args, opts, cb) { + f.execFile = (program, args, opts, cb) => { if (/sys\.executable/.test(args[args.length - 1])) { cb(new Error('not found')) } else if (/sys\.version_info/.test(args[args.length - 1])) { @@ -222,10 +211,7 @@ test('find python - no python, no python launcher, bad guess', function (t) { t.fail() } } - f.findPython() - function done (err) { - t.ok(/Could not find any Python/.test(err)) - t.ok(/not in PATH/.test(f.errorLog)) - } + await t.rejects(() => f.findPython(), /Could not find any Python/) + t.ok(/not in PATH/.test(f.errorLog)) }) diff --git a/test/test-find-visualstudio.js b/test/test-find-visualstudio.js index 1327cf8841..1ad3adcd50 100644 --- a/test/test-find-visualstudio.js +++ b/test/test-find-visualstudio.js @@ -1,10 +1,9 @@ 'use strict' -const test = require('tap').test -const fs = require('fs') +const { test } = require('tap') +const fs = require('fs').promises const path = require('path') -const findVisualStudio = require('../lib/find-visualstudio') -const VisualStudioFinder = findVisualStudio.test.VisualStudioFinder +const { VisualStudioFinder } = require('../lib/find-visualstudio').test const semverV1 = { major: 1, minor: 0, patch: 0 } @@ -15,7 +14,7 @@ function poison (object, property) { console.error(Error(`Property ${property} should not have been accessed.`)) process.abort() } - var descriptor = { + const descriptor = { configurable: false, enumerable: false, get: fail, @@ -24,79 +23,75 @@ function poison (object, property) { Object.defineProperty(object, property, descriptor) } -function TestVisualStudioFinder () { VisualStudioFinder.apply(this, arguments) } -TestVisualStudioFinder.prototype = Object.create(VisualStudioFinder.prototype) -// Silence npmlog - remove for debugging -TestVisualStudioFinder.prototype.log = { - silly: () => {}, - verbose: () => {}, - info: () => {}, - warn: () => {}, - error: () => {} +class TestVisualStudioFinder extends VisualStudioFinder { + constructor (...args) { + super(...args) + + // Silence npmlog - remove for debugging + this.log = { + silly: () => {}, + verbose: () => {}, + info: () => {}, + warn: () => {}, + error: () => {} + } + } } -test('VS2013', function (t) { - t.plan(4) - - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info, { - msBuild: 'C:\\MSBuild12\\MSBuild.exe', - path: 'C:\\VS2013', - sdk: null, - toolset: 'v120', - version: '12.0', - versionMajor: 12, - versionMinor: 0, - versionYear: 2013 - }) - }) +test('VS2013', async (t) => { + t.plan(3) - finder.findVisualStudio2017OrNewer = (cb) => { - finder.parseData(new Error(), '', '', cb) - } - finder.regSearchKeys = (keys, value, addOpts, cb) => { - for (var i = 0; i < keys.length; ++i) { - const fullName = `${keys[i]}\\${value}` + const finder = new TestVisualStudioFinder(semverV1, null) + + finder.findVisualStudio2017OrNewer = async () => null + + finder.regSearchKeys = async (keys, value, addOpts) => { + for (const key of keys) { + const fullName = `${key}\\${value}` switch (fullName) { case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': continue case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0': t.pass(`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': t.pass(`expected search for registry value ${fullName}`) - return cb(null, 'C:\\MSBuild12\\') + return 'C:\\MSBuild12\\' default: t.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info, { + msBuild: 'C:\\MSBuild12\\MSBuild.exe', + path: 'C:\\VS2013', + sdk: null, + toolset: 'v120', + version: '12.0', + versionMajor: 12, + versionMinor: 0, + versionYear: 2013 + }) }) -test('VS2013 should not be found on new node versions', function (t) { - t.plan(2) +test('VS2013 should not be found on new node versions', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder({ - major: 10, - minor: 0, - patch: 0 - }, null, (err, info) => { - t.ok(/find .* Visual Studio/i.test(err), 'expect error') - t.false(info, 'no data') - }) + const finder = new TestVisualStudioFinder({ major: 10, minor: 0, patch: 0 }, null) - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2017_Unusable.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.regSearchKeys = (keys, value, addOpts, cb) => { - for (var i = 0; i < keys.length; ++i) { - const fullName = `${keys[i]}\\${value}` + + finder.regSearchKeys = async (keys, value, addOpts) => { + for (const key of keys) { + const fullName = `${key}\\${value}` switch (fullName) { case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': case 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': @@ -105,111 +100,95 @@ test('VS2013 should not be found on new node versions', function (t) { t.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() + + return t.rejects(() => finder.findVisualStudio(), /find .* Visual Studio/i, 'expect error') }) -test('VS2015', function (t) { - t.plan(4) - - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info, { - msBuild: 'C:\\MSBuild14\\MSBuild.exe', - path: 'C:\\VS2015', - sdk: null, - toolset: 'v140', - version: '14.0', - versionMajor: 14, - versionMinor: 0, - versionYear: 2015 - }) - }) +test('VS2015', async (t) => { + t.plan(3) - finder.findVisualStudio2017OrNewer = (cb) => { - finder.parseData(new Error(), '', '', cb) - } - finder.regSearchKeys = (keys, value, addOpts, cb) => { - for (var i = 0; i < keys.length; ++i) { - const fullName = `${keys[i]}\\${value}` + const finder = new TestVisualStudioFinder(semverV1, null) + + finder.findVisualStudio2017OrNewer = (cb) => null + + finder.regSearchKeys = async (keys, value, addOpts) => { + for (const key of keys) { + const fullName = `${key}\\${value}` switch (fullName) { case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': t.pass(`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': t.pass(`expected search for registry value ${fullName}`) - return cb(null, 'C:\\MSBuild14\\') + return 'C:\\MSBuild14\\' default: t.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } - finder.findVisualStudio() -}) -test('error from PowerShell', function (t) { - t.plan(2) - - const finder = new TestVisualStudioFinder(semverV1, null, null) - - finder.parseData(new Error(), '', '', (info) => { - t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') - t.false(info, 'no data') + const info = await finder.findVisualStudio() + t.deepEqual(info, { + msBuild: 'C:\\MSBuild14\\MSBuild.exe', + path: 'C:\\VS2015', + sdk: null, + toolset: 'v140', + version: '14.0', + versionMajor: 14, + versionMinor: 0, + versionYear: 2015 }) }) -test('empty output from PowerShell', function (t) { +test('empty output from PowerShell', (t) => { t.plan(2) - const finder = new TestVisualStudioFinder(semverV1, null, null) + const finder = new TestVisualStudioFinder(semverV1, null) - finder.parseData(null, '', '', (info) => { - t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') - t.false(info, 'no data') - }) + const info = finder.parseData(null, '', '') + t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') + t.false(info, 'no data') }) -test('output from PowerShell not JSON', function (t) { +test('output from PowerShell not JSON', (t) => { t.plan(2) - const finder = new TestVisualStudioFinder(semverV1, null, null) + const finder = new TestVisualStudioFinder(semverV1, null) - finder.parseData(null, 'AAAABBBB', '', (info) => { - t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') - t.false(info, 'no data') - }) + const info = finder.parseData(null, 'AAAABBBB', '') + t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') + t.false(info, 'no data') }) -test('wrong JSON from PowerShell', function (t) { +test('wrong JSON from PowerShell', (t) => { t.plan(2) const finder = new TestVisualStudioFinder(semverV1, null, null) - finder.parseData(null, '{}', '', (info) => { - t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') - t.false(info, 'no data') - }) + const info = finder.parseData(null, '{}', '') + t.ok(/use PowerShell/i.test(finder.errorLog[0]), 'expect error') + t.false(info, 'no data') }) -test('empty JSON from PowerShell', function (t) { +test('empty JSON from PowerShell', (t) => { t.plan(2) const finder = new TestVisualStudioFinder(semverV1, null, null) - finder.parseData(null, '[]', '', (info) => { - t.ok(/find .* Visual Studio/i.test(finder.errorLog[0]), 'expect error') - t.false(info, 'no data') - }) + const info = finder.parseData('[]', '') + t.ok(/find .* Visual Studio/i.test(finder.errorLog[0]), 'expect error') + t.false(info, 'no data') }) -test('future version', function (t) { +test('future version', (t) => { t.plan(3) const finder = new TestVisualStudioFinder(semverV1, null, null) - finder.parseData(null, JSON.stringify([{ + const info = finder.parseData(JSON.stringify([{ packages: [ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', @@ -217,460 +196,423 @@ test('future version', function (t) { ], path: 'C:\\VS', version: '9999.9999.9999.9999' - }]), '', (info) => { - t.ok(/unknown version/i.test(finder.errorLog[0]), 'expect error') - t.ok(/find .* Visual Studio/i.test(finder.errorLog[1]), 'expect error') - t.false(info, 'no data') - }) + }]), '') + + t.ok(/unknown version/i.test(finder.errorLog[0]), 'expect error') + t.ok(/find .* Visual Studio/i.test(finder.errorLog[1]), 'expect error') + t.false(info, 'no data') }) -test('single unusable VS2017', function (t) { +test('single unusable VS2017', async (t) => { t.plan(3) const finder = new TestVisualStudioFinder(semverV1, null, null) - const file = path.join(__dirname, 'fixtures', 'VS_2017_Unusable.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', (info) => { - t.ok(/checking/i.test(finder.errorLog[0]), 'expect error') - t.ok(/find .* Visual Studio/i.test(finder.errorLog[2]), 'expect error') - t.false(info, 'no data') - }) + const file = path.join(__dirname, 'fixtures/VS_2017_Unusable.txt') + const data = await fs.readFile(file) + const info = finder.parseData(data, '') + t.ok(/checking/i.test(finder.errorLog[0]), 'expect error') + t.ok(/find .* Visual Studio/i.test(finder.errorLog[2]), 'expect error') + t.false(info, 'no data') }) -test('minimal VS2017 Build Tools', function (t) { - t.plan(2) +test('minimal VS2017 Build Tools', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 - }) - }) + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', - 'VS_2017_BuildTools_minimal.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2017_BuildTools_minimal.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + + t.deepEqual(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 + }) }) -test('VS2017 Community with C++ workload', function (t) { - t.plan(2) +test('VS2017 Community with C++ workload', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', - 'VS_2017_Community_workload.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2017_Community_workload.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + + t.deepEqual(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 + }) }) -test('VS2017 Express', function (t) { - t.plan(2) +test('VS2017 Express', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', 'VS_2017_Express.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2017_Express.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + + t.deepEqual(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 + }) }) -test('VS2019 Preview with C++ workload', function (t) { - t.plan(2) +test('VS2019 Preview with C++ workload', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', - 'VS_2019_Preview.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2019_Preview.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + + t.deepEqual(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 + }) }) -test('minimal VS2019 Build Tools', function (t) { - t.plan(2) +test('minimal VS2019 Build Tools', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', - 'VS_2019_BuildTools_minimal.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2019_BuildTools_minimal.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() -}) - -test('VS2019 Community with C++ workload', function (t) { - t.plan(2) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(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 info = await finder.findVisualStudio() + + t.deepEqual(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 }) +}) +test('VS2019 Community with C++ workload', async (t) => { + t.plan(1) + + const finder = new TestVisualStudioFinder(semverV1, null) poison(finder, 'regSearchKeys') - finder.findVisualStudio2017OrNewer = (cb) => { - const file = path.join(__dirname, 'fixtures', - 'VS_2019_Community_workload.txt') - const data = fs.readFileSync(file) - finder.parseData(null, data, '', cb) + + finder.findVisualStudio2017OrNewer = async () => { + const file = path.join(__dirname, 'fixtures/VS_2019_Community_workload.txt') + const data = await fs.readFile(file) + return finder.parseData(data, '') } - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + + t.deepEqual(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 + }) }) function allVsVersions (t, finder) { - finder.findVisualStudio2017OrNewer = (cb) => { - const data0 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2017_Unusable.txt'))) - const data1 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2017_BuildTools_minimal.txt'))) - const data2 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2017_Community_workload.txt'))) - const data3 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2017_Express.txt'))) - const data4 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2019_Preview.txt'))) - const data5 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2019_BuildTools_minimal.txt'))) - const data6 = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', - 'VS_2019_Community_workload.txt'))) - const data = JSON.stringify(data0.concat(data1, data2, data3, data4, - data5, data6)) - finder.parseData(null, data, '', cb) + finder.findVisualStudio2017OrNewer = async () => { + const data = JSON.stringify((await Promise.all([ + 'VS_2017_Unusable.txt', + 'VS_2017_BuildTools_minimal.txt', + 'VS_2017_Community_workload.txt', + 'VS_2017_Express.txt', + 'VS_2019_Preview.txt', + 'VS_2019_BuildTools_minimal.txt', + 'VS_2019_Community_workload.txt' + ].map((f) => fs.readFile(path.join(__dirname, `fixtures/${f}`))))) + .map((c) => JSON.parse(c)) + .reduce((p, c) => p.concat(c), [])) + return finder.parseData(data, '') } - finder.regSearchKeys = (keys, value, addOpts, cb) => { - for (var i = 0; i < keys.length; ++i) { - const fullName = `${keys[i]}\\${value}` + + finder.regSearchKeys = async (keys, value, addOpts) => { + for (const key of keys) { + const fullName = `${key}\\${value}` switch (fullName) { case 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0': 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: t.fail(`unexpected search for registry value ${fullName}`) } } - return cb(new Error()) + throw new Error() } } -test('fail when looking for invalid path', function (t) { - t.plan(2) +test('fail when looking for invalid path', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, 'AABB', (err, info) => { - t.ok(/find .* Visual Studio/i.test(err), 'expect error') - t.false(info, 'no data') - }) + const finder = new TestVisualStudioFinder(semverV1, 'AABB') allVsVersions(t, finder) - finder.findVisualStudio() + + return t.rejects(() => finder.findVisualStudio(), /find .* Visual Studio/i, 'expect error') }) -test('look for VS2013 by version number', function (t) { - t.plan(2) +test('look for VS2013 by version number', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, '2013', (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.versionYear, 2013) - }) + const finder = new TestVisualStudioFinder(semverV1, '2013') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.versionYear, 2013) }) -test('look for VS2013 by installation path', function (t) { - t.plan(2) +test('look for VS2013 by installation path', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, 'C:\\VS2013') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, 'C:\\VS2013') }) -test('look for VS2015 by version number', function (t) { - t.plan(2) +test('look for VS2015 by version number', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, '2015', (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.versionYear, 2015) - }) + const finder = new TestVisualStudioFinder(semverV1, '2015') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.versionYear, 2015) }) -test('look for VS2015 by installation path', function (t) { - t.plan(2) +test('look for VS2015 by installation path', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, 'C:\\VS2015') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, 'C:\\VS2015') }) -test('look for VS2017 by version number', function (t) { - t.plan(2) +test('look for VS2017 by version number', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, '2017', (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.versionYear, 2017) - }) + const finder = new TestVisualStudioFinder(semverV1, '2017') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.versionYear, 2017) }) -test('look for VS2017 by installation path', function (t) { - t.plan(2) +test('look for VS2017 by installation path', async (t) => { + t.plan(1) const finder = new TestVisualStudioFinder(semverV1, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') - }) + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') }) -test('look for VS2019 by version number', function (t) { - t.plan(2) +test('look for VS2019 by version number', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, '2019', (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.versionYear, 2019) - }) + const finder = new TestVisualStudioFinder(semverV1, '2019') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.versionYear, 2019) }) -test('look for VS2019 by installation path', function (t) { - t.plan(2) +test('look for VS2019 by installation path', async (t) => { + t.plan(1) const finder = new TestVisualStudioFinder(semverV1, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') allVsVersions(t, finder) - finder.findVisualStudio() + const info = await finder.findVisualStudio() + t.deepEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) -test('msvs_version match should be case insensitive', function (t) { - t.plan(2) +test('msvs_version match should be case insensitive', async (t) => { + t.plan(1) const finder = new TestVisualStudioFinder(semverV1, - 'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + 'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) -test('latest version should be found by default', function (t) { - t.plan(2) +test('latest version should be found by default', async (t) => { + t.plan(1) - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.versionYear, 2019) - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.versionYear, 2019) }) -test('run on a usable VS Command Prompt', function (t) { - t.plan(2) +test('run on a usable VS Command Prompt', async (t) => { + t.plan(1) 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) => { - t.strictEqual(err, null) - t.deepEqual(info.path, 'C:\\VS2015') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, 'C:\\VS2015') }) -test('VCINSTALLDIR match should be case insensitive', function (t) { - t.plan(2) +test('VCINSTALLDIR match should be case insensitive', async (t) => { + t.plan(1) process.env.VCINSTALLDIR = 'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS\\VC' - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, - 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, + 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') }) -test('run on a unusable VS Command Prompt', function (t) { - t.plan(2) +test('run on a unusable VS Command Prompt', async (t) => { + t.plan(1) process.env.VCINSTALLDIR = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildToolsUnusable\\VC' - const finder = new TestVisualStudioFinder(semverV1, null, (err, info) => { - t.ok(/find .* Visual Studio/i.test(err), 'expect error') - t.false(info, 'no data') - }) + const finder = new TestVisualStudioFinder(semverV1, null) allVsVersions(t, finder) - finder.findVisualStudio() + + return t.rejects(() => finder.findVisualStudio(), /find .* Visual Studio/i, 'expect error') }) -test('run on a VS Command Prompt with matching msvs_version', function (t) { - t.plan(2) +test('run on a VS Command Prompt with matching msvs_version', async (t) => { + t.plan(1) process.env.VCINSTALLDIR = 'C:\\VS2015\\VC' - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015', - (err, info) => { - t.strictEqual(err, null) - t.deepEqual(info.path, 'C:\\VS2015') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(t, finder) - finder.findVisualStudio() + + const info = await finder.findVisualStudio() + t.deepEqual(info.path, 'C:\\VS2015') }) -test('run on a VS Command Prompt with mismatched msvs_version', function (t) { - t.plan(2) +test('run on a VS Command Prompt with mismatched msvs_version', async (t) => { + t.plan(1) process.env.VCINSTALLDIR = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC' - const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015', - (err, info) => { - t.ok(/find .* Visual Studio/i.test(err), 'expect error') - t.false(info, 'no data') - }) + const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') allVsVersions(t, finder) - finder.findVisualStudio() + + return t.rejects(() => finder.findVisualStudio(), /find .* Visual Studio/i, 'expect error') }) diff --git a/test/test-install.js b/test/test-install.js index c3317155e0..23e3aac078 100644 --- a/test/test-install.js +++ b/test/test-install.js @@ -1,34 +1,34 @@ 'use strict' -const test = require('tap').test -const install = require('../lib/install').test.install +const { test } = require('tap') +const { install } = require('../lib/install').test require('npmlog').level = 'error' // we expect a warning -test('EACCES retry once', function (t) { +test('EACCES retry once', (t) => { t.plan(3) - var fs = {} - fs.stat = function (path, cb) { - var err = new Error() + const fs = {} + fs.stat = (path, cb) => { + const err = new Error() err.code = 'EACCES' cb(err) t.ok(true) } - var gyp = {} + const gyp = {} gyp.devDir = __dirname gyp.opts = {} gyp.opts.ensure = true gyp.commands = {} - gyp.commands.install = function (argv, cb) { + gyp.commands.install = (argv, cb) => { install(fs, gyp, argv, cb) } - gyp.commands.remove = function (argv, cb) { + gyp.commands.remove = (argv, cb) => { cb() } - gyp.commands.install([], function (err) { + gyp.commands.install([], (err) => { t.ok(true) if (/"pre" versions of node cannot be installed/.test(err.message)) { t.ok(true) diff --git a/test/test-options.js b/test/test-options.js index 252baa2035..7ab23e188a 100644 --- a/test/test-options.js +++ b/test/test-options.js @@ -1,15 +1,15 @@ 'use strict' -const test = require('tap').test -const gyp = require('../lib/node-gyp') +const { test } = require('tap') +const Gyp = require('../lib/node-gyp') -test('options in environment', function (t) { +test('options in environment', (t) => { t.plan(1) // `npm test` dumps a ton of npm_config_* variables in the environment. Object.keys(process.env) - .filter(function (key) { return /^npm_config_/.test(key) }) - .forEach(function (key) { delete process.env[key] }) + .filter((key) => /^npm_config_/.test(key)) + .forEach((key) => delete process.env[key]) // Zero-length keys should get filtered out. process.env.npm_config_ = '42' @@ -18,8 +18,8 @@ test('options in environment', function (t) { // Except loglevel. process.env.npm_config_loglevel = 'debug' - var g = gyp() - g.parseArgv(['rebuild']) // Also sets opts.argv. + const gyp = new Gyp() + gyp.parseArgv(['rebuild']) // Also sets opts.argv. - t.deepEqual(Object.keys(g.opts).sort(), ['argv', 'x']) + t.deepEqual(Object.keys(gyp.opts).sort(), ['argv', 'x']) }) diff --git a/test/test-process-release.js b/test/test-process-release.js index c3ee0703c5..df064e9ef3 100644 --- a/test/test-process-release.js +++ b/test/test-process-release.js @@ -1,12 +1,12 @@ 'use strict' -const test = require('tap').test +const { test } = require('tap') const processRelease = require('../lib/process-release') -test('test process release - process.version = 0.8.20', function (t) { +test('test process release - process.version = 0.8.20', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.8.20', null) + const release = processRelease([], { opts: {} }, 'v0.8.20', null) t.equal(release.semver.version, '0.8.20') delete release.semver @@ -24,10 +24,10 @@ test('test process release - process.version = 0.8.20', function (t) { }) }) -test('test process release - process.version = 0.10.21', function (t) { +test('test process release - process.version = 0.10.21', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.10.21', null) + const release = processRelease([], { opts: {} }, 'v0.10.21', null) t.equal(release.semver.version, '0.10.21') delete release.semver @@ -46,10 +46,10 @@ test('test process release - process.version = 0.10.21', function (t) { }) // prior to -headers.tar.gz -test('test process release - process.version = 0.12.9', function (t) { +test('test process release - process.version = 0.12.9', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.12.9', null) + const release = processRelease([], { opts: {} }, 'v0.12.9', null) t.equal(release.semver.version, '0.12.9') delete release.semver @@ -68,10 +68,10 @@ test('test process release - process.version = 0.12.9', function (t) { }) // prior to -headers.tar.gz -test('test process release - process.version = 0.10.41', function (t) { +test('test process release - process.version = 0.10.41', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.10.41', null) + const release = processRelease([], { opts: {} }, 'v0.10.41', null) t.equal(release.semver.version, '0.10.41') delete release.semver @@ -90,10 +90,10 @@ test('test process release - process.version = 0.10.41', function (t) { }) // has -headers.tar.gz -test('test process release - process.release ~ node@0.10.42', function (t) { +test('test process release - process.release ~ node@0.10.42', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.10.42', null) + const release = processRelease([], { opts: {} }, 'v0.10.42', null) t.equal(release.semver.version, '0.10.42') delete release.semver @@ -112,10 +112,10 @@ test('test process release - process.release ~ node@0.10.42', function (t) { }) // has -headers.tar.gz -test('test process release - process.release ~ node@0.12.10', function (t) { +test('test process release - process.release ~ node@0.12.10', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v0.12.10', null) + const release = processRelease([], { opts: {} }, 'v0.12.10', null) t.equal(release.semver.version, '0.12.10') delete release.semver @@ -133,10 +133,10 @@ test('test process release - process.release ~ node@0.12.10', function (t) { }) }) -test('test process release - process.release ~ node@4.1.23', function (t) { +test('test process release - process.release ~ node@4.1.23', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v4.1.23', { + const release = processRelease([], { opts: {} }, 'v4.1.23', { name: 'node', headersUrl: 'https://nodejs.org/dist/v4.1.23/node-v4.1.23-headers.tar.gz' }) @@ -157,10 +157,10 @@ test('test process release - process.release ~ node@4.1.23', function (t) { }) }) -test('test process release - process.release ~ node@4.1.23 / corp build', function (t) { +test('test process release - process.release ~ node@4.1.23 / corp build', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v4.1.23', { + const release = processRelease([], { opts: {} }, 'v4.1.23', { name: 'node', headersUrl: 'https://some.custom.location/node-v4.1.23-headers.tar.gz' }) @@ -181,10 +181,10 @@ test('test process release - process.release ~ node@4.1.23 / corp build', functi }) }) -test('test process release - process.release ~ node@12.8.0 Windows', function (t) { +test('test process release - process.release ~ node@12.8.0 Windows', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v12.8.0', { + const release = processRelease([], { opts: {} }, 'v12.8.0', { name: 'node', sourceUrl: 'https://nodejs.org/download/release/v12.8.0/node-v12.8.0.tar.gz', headersUrl: 'https://nodejs.org/download/release/v12.8.0/node-v12.8.0-headers.tar.gz', @@ -207,10 +207,10 @@ test('test process release - process.release ~ node@12.8.0 Windows', function (t }) }) -test('test process release - process.release ~ node@12.8.0 Windows ARM64', function (t) { +test('test process release - process.release ~ node@12.8.0 Windows ARM64', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v12.8.0', { + const release = processRelease([], { opts: {} }, 'v12.8.0', { name: 'node', sourceUrl: 'https://unofficial-builds.nodejs.org/download/release/v12.8.0/node-v12.8.0.tar.gz', headersUrl: 'https://unofficial-builds.nodejs.org/download/release/v12.8.0/node-v12.8.0-headers.tar.gz', @@ -233,10 +233,10 @@ test('test process release - process.release ~ node@12.8.0 Windows ARM64', funct }) }) -test('test process release - process.release ~ node@4.1.23 --target=0.10.40', function (t) { +test('test process release - process.release ~ node@4.1.23 --target=0.10.40', (t) => { t.plan(2) - var release = processRelease([], { opts: { target: '0.10.40' } }, 'v4.1.23', { + const release = processRelease([], { opts: { target: '0.10.40' } }, 'v4.1.23', { name: 'node', headersUrl: 'https://nodejs.org/dist/v4.1.23/node-v4.1.23-headers.tar.gz' }) @@ -257,10 +257,10 @@ test('test process release - process.release ~ node@4.1.23 --target=0.10.40', fu }) }) -test('test process release - process.release ~ node@4.1.23 --dist-url=https://foo.bar/baz', function (t) { +test('test process release - process.release ~ node@4.1.23 --dist-url=https://foo.bar/baz', (t) => { t.plan(2) - var release = processRelease([], { opts: { 'dist-url': 'https://foo.bar/baz' } }, 'v4.1.23', { + const release = processRelease([], { opts: { 'dist-url': 'https://foo.bar/baz' } }, 'v4.1.23', { name: 'node', headersUrl: 'https://nodejs.org/dist/v4.1.23/node-v4.1.23-headers.tar.gz' }) @@ -281,10 +281,10 @@ test('test process release - process.release ~ node@4.1.23 --dist-url=https://fo }) }) -test('test process release - process.release ~ frankenstein@4.1.23', function (t) { +test('test process release - process.release ~ frankenstein@4.1.23', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v4.1.23', { + const release = processRelease([], { opts: {} }, 'v4.1.23', { name: 'frankenstein', headersUrl: 'https://frankensteinjs.org/dist/v4.1.23/frankenstein-v4.1.23-headers.tar.gz' }) @@ -305,10 +305,10 @@ test('test process release - process.release ~ frankenstein@4.1.23', function (t }) }) -test('test process release - process.release ~ frankenstein@4.1.23 --dist-url=http://foo.bar/baz/', function (t) { +test('test process release - process.release ~ frankenstein@4.1.23 --dist-url=http://foo.bar/baz/', (t) => { t.plan(2) - var release = processRelease([], { opts: { 'dist-url': 'http://foo.bar/baz/' } }, 'v4.1.23', { + const release = processRelease([], { opts: { 'dist-url': 'http://foo.bar/baz/' } }, 'v4.1.23', { name: 'frankenstein', headersUrl: 'https://frankensteinjs.org/dist/v4.1.23/frankenstein-v4.1.23.tar.gz' }) @@ -329,10 +329,10 @@ test('test process release - process.release ~ frankenstein@4.1.23 --dist-url=ht }) }) -test('test process release - process.release ~ node@4.0.0-rc.4', function (t) { +test('test process release - process.release ~ node@4.0.0-rc.4', (t) => { t.plan(2) - var release = processRelease([], { opts: {} }, 'v4.0.0-rc.4', { + const release = processRelease([], { opts: {} }, 'v4.0.0-rc.4', { name: 'node', headersUrl: 'https://nodejs.org/download/rc/v4.0.0-rc.4/node-v4.0.0-rc.4-headers.tar.gz' }) @@ -353,12 +353,12 @@ test('test process release - process.release ~ node@4.0.0-rc.4', function (t) { }) }) -test('test process release - process.release ~ node@4.0.0-rc.4 passed as argv[0]', function (t) { +test('test process release - process.release ~ node@4.0.0-rc.4 passed as argv[0]', (t) => { t.plan(2) // note the missing 'v' on the arg, it should normalise when checking // whether we're on the default or not - var release = processRelease(['4.0.0-rc.4'], { opts: {} }, 'v4.0.0-rc.4', { + const release = processRelease(['4.0.0-rc.4'], { opts: {} }, 'v4.0.0-rc.4', { name: 'node', headersUrl: 'https://nodejs.org/download/rc/v4.0.0-rc.4/node-v4.0.0-rc.4-headers.tar.gz' }) @@ -379,12 +379,12 @@ test('test process release - process.release ~ node@4.0.0-rc.4 passed as argv[0] }) }) -test('test process release - process.release ~ node@4.0.0-rc.4 - bogus string passed as argv[0]', function (t) { +test('test process release - process.release ~ node@4.0.0-rc.4 - bogus string passed as argv[0]', (t) => { t.plan(2) // additional arguments can be passed in on the commandline that should be ignored if they // are not specifying a valid version @ position 0 - var release = processRelease(['this is no version!'], { opts: {} }, 'v4.0.0-rc.4', { + const release = processRelease(['this is no version!'], { opts: {} }, 'v4.0.0-rc.4', { name: 'node', headersUrl: 'https://nodejs.org/download/rc/v4.0.0-rc.4/node-v4.0.0-rc.4-headers.tar.gz' }) @@ -405,12 +405,12 @@ test('test process release - process.release ~ node@4.0.0-rc.4 - bogus string pa }) }) -test('test process release - NODEJS_ORG_MIRROR', function (t) { +test('test process release - NODEJS_ORG_MIRROR', (t) => { t.plan(2) process.env.NODEJS_ORG_MIRROR = 'http://foo.bar' - var release = processRelease([], { opts: {} }, 'v4.1.23', { + const release = processRelease([], { opts: {} }, 'v4.1.23', { name: 'node', headersUrl: 'https://nodejs.org/dist/v4.1.23/node-v4.1.23-headers.tar.gz' })