feat: convert internal classes from util.inherits to classes

BREAKING CHANGE: the `Gyp` class exported is now created using
ECMAScript classes and therefore might have small differences to classes
that were previously created with `util.inherits`.
This commit is contained in:
Luke Karrys 2023-10-27 16:40:36 -07:00
parent 355622f4aa
commit d52997e975
7 changed files with 307 additions and 344 deletions

View file

@ -9,8 +9,8 @@ const win = process.platform === 'win32'
const findNodeDirectory = require('./find-node-directory') const findNodeDirectory = require('./find-node-directory')
const createConfigGypi = require('./create-config-gypi') const createConfigGypi = require('./create-config-gypi')
const { format: msgFormat } = require('util') const { format: msgFormat } = require('util')
const findPython = require('./find-python') const { findPython } = require('./find-python')
const findVisualStudio = win ? require('./find-visualstudio') : null const { findVisualStudio } = win ? require('./find-visualstudio') : {}
async function configure (gyp, argv) { async function configure (gyp, argv) {
const buildDir = path.resolve('build') const buildDir = path.resolve('build')

View file

@ -2,10 +2,15 @@
const log = require('./log') const log = require('./log')
const semver = require('semver') const semver = require('semver')
const { _extend: extend } = require('util') // eslint-disable-line n/no-deprecated-api
const { execFile } = require('./util') const { execFile } = require('./util')
const win = process.platform === 'win32' const win = process.platform === 'win32'
function getOsUserInfo () {
try {
return require('os').userInfo().username
} catch {}
}
const systemDrive = process.env.SystemDrive || 'C:' const systemDrive = process.env.SystemDrive || 'C:'
const username = process.env.USERNAME || process.env.USER || getOsUserInfo() const username = process.env.USERNAME || process.env.USER || getOsUserInfo()
const localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local` const localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local`
@ -32,40 +37,36 @@ for (const majorMinor of ['311', '310', '39', '38']) {
} }
} }
function getOsUserInfo () { class PythonFinder {
try { static findPython = (...args) => new PythonFinder(...args).findPython()
return require('os').userInfo().username
} catch (e) {}
}
function PythonFinder (configPython) { log = log.withPrefix('find Python')
this.configPython = configPython argsExecutable = ['-c', 'import sys; print(sys.executable);']
this.errorLog = [] argsVersion = ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);']
} semverRange = '>=3.6.0'
PythonFinder.prototype = {
log: log.withPrefix('find Python'),
argsExecutable: ['-c', 'import sys; print(sys.executable);'],
argsVersion: ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'],
semverRange: '>=3.6.0',
// These can be overridden for testing: // These can be overridden for testing:
execFile, execFile = execFile
env: process.env, env = process.env
win, win = win
pyLauncher: 'py.exe', pyLauncher = 'py.exe'
winDefaultLocations: winDefaultLocationsArray, winDefaultLocations = winDefaultLocationsArray
constructor (configPython) {
this.configPython = configPython
this.errorLog = []
}
// Logs a message at verbose level, but also saves it to be displayed later // 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. // at error level if an error occurs. This should help diagnose the problem.
addLog: function addLog (message) { addLog (message) {
this.log.verbose(message) this.log.verbose(message)
this.errorLog.push(message) this.errorLog.push(message)
}, }
// Find Python by trying a sequence of possibilities. // Find Python by trying a sequence of possibilities.
// Ignore errors, keep trying until Python is found. // Ignore errors, keep trying until Python is found.
findPython: async function findPython () { async findPython () {
const SKIP = 0 const SKIP = 0
const FAIL = 1 const FAIL = 1
const toCheck = (() => { const toCheck = (() => {
@ -161,12 +162,12 @@ PythonFinder.prototype = {
} }
return this.fail() return this.fail()
}, }
// Check if command is a valid Python to use. // Check if command is a valid Python to use.
// Will exit the Python finder on success. // Will exit the Python finder on success.
// If on Windows, run in a CMD shell to support BAT/CMD launchers. // If on Windows, run in a CMD shell to support BAT/CMD launchers.
checkCommand: async function checkCommand (command) { async checkCommand (command) {
let exec = command let exec = command
let args = this.argsExecutable let args = this.argsExecutable
let shell = false let shell = false
@ -190,7 +191,7 @@ PythonFinder.prototype = {
this.addLog(`- "${command}" is not in PATH or produced an error`) this.addLog(`- "${command}" is not in PATH or produced an error`)
throw err throw err
} }
}, }
// Check if the py launcher can find a valid Python to use. // Check if the py launcher can find a valid Python to use.
// Will exit the Python finder on success. // Will exit the Python finder on success.
@ -202,7 +203,7 @@ PythonFinder.prototype = {
// the first command line argument. Since "py.exe -3" would be an invalid // the first command line argument. Since "py.exe -3" would be an invalid
// executable for "execFile", we have to use the launcher to figure out // executable for "execFile", we have to use the launcher to figure out
// where the actual "python.exe" executable is located. // where the actual "python.exe" executable is located.
checkPyLauncher: async function checkPyLauncher () { async checkPyLauncher () {
this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`) this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`)
// Possible outcomes: same as checkCommand // Possible outcomes: same as checkCommand
try { try {
@ -213,11 +214,11 @@ PythonFinder.prototype = {
this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`) this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`)
throw err throw err
} }
}, }
// Check if a Python executable is the correct version to use. // Check if a Python executable is the correct version to use.
// Will exit the Python finder on success. // Will exit the Python finder on success.
checkExecPath: async function checkExecPath (execPath) { async checkExecPath (execPath) {
this.log.verbose(`- executing "${execPath}" to get version`) this.log.verbose(`- executing "${execPath}" to get version`)
// Possible outcomes: // Possible outcomes:
// - Error: executable can not be run (likely meaning the command wasn't // - Error: executable can not be run (likely meaning the command wasn't
@ -249,11 +250,11 @@ PythonFinder.prototype = {
this.addLog(`- "${execPath}" could not be run`) this.addLog(`- "${execPath}" could not be run`)
throw err throw err
} }
}, }
// Run an executable or shell command, trimming the output. // Run an executable or shell command, trimming the output.
run: async function run (exec, args, shell) { async run (exec, args, shell) {
const env = extend({}, this.env) const env = Object.assign({}, this.env)
env.TERM = 'dumb' env.TERM = 'dumb'
const opts = { env, shell } const opts = { env, shell }
@ -270,14 +271,14 @@ PythonFinder.prototype = {
this.log.silly('execFile: threw:\n%s', err.stack) this.log.silly('execFile: threw:\n%s', err.stack)
throw err throw err
} }
}, }
succeed: function succeed (execPath, version) { succeed (execPath, version) {
this.log.info(`using Python version ${version} found at "${execPath}"`) this.log.info(`using Python version ${version} found at "${execPath}"`)
return execPath return execPath
}, }
fail: function fail () { fail () {
const errorLog = this.errorLog.join('\n') const errorLog = this.errorLog.join('\n')
const pathExample = this.win const pathExample = this.win
@ -306,10 +307,4 @@ PythonFinder.prototype = {
} }
} }
const findPython = async (configPython) => new PythonFinder(configPython).findPython() module.exports = PythonFinder
module.exports = findPython
module.exports.test = {
PythonFinder,
findPython
}

View file

@ -5,26 +5,28 @@ const { existsSync } = require('fs')
const { win32: path } = require('path') const { win32: path } = require('path')
const { regSearchKeys, execFile } = require('./util') const { regSearchKeys, execFile } = require('./util')
function VisualStudioFinder (nodeSemver, configMsvsVersion) { class VisualStudioFinder {
this.nodeSemver = nodeSemver static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio()
this.configMsvsVersion = configMsvsVersion
this.errorLog = []
this.validVersions = []
}
VisualStudioFinder.prototype = { log = log.withPrefix('find VS')
log: log.withPrefix('find VS'),
regSearchKeys, regSearchKeys = regSearchKeys
constructor (nodeSemver, configMsvsVersion) {
this.nodeSemver = nodeSemver
this.configMsvsVersion = configMsvsVersion
this.errorLog = []
this.validVersions = []
}
// Logs a message at verbose level, but also saves it to be displayed later // 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. // at error level if an error occurs. This should help diagnose the problem.
addLog: function addLog (message) { addLog (message) {
this.log.verbose(message) this.log.verbose(message)
this.errorLog.push(message) this.errorLog.push(message)
}, }
findVisualStudio: async function findVisualStudio () { async findVisualStudio () {
this.configVersionYear = null this.configVersionYear = null
this.configPath = null this.configPath = null
if (this.configMsvsVersion) { if (this.configMsvsVersion) {
@ -65,16 +67,16 @@ VisualStudioFinder.prototype = {
} }
return this.fail() return this.fail()
}, }
succeed: function succeed (info) { succeed (info) {
this.log.info(`using VS${info.versionYear} (${info.version}) found at:` + this.log.info(`using VS${info.versionYear} (${info.version}) found at:` +
`\n"${info.path}"` + `\n"${info.path}"` +
'\nrun with --verbose for detailed information') '\nrun with --verbose for detailed information')
return info return info
}, }
fail: function fail () { fail () {
if (this.configMsvsVersion && this.envVcInstallDir) { if (this.configMsvsVersion && this.envVcInstallDir) {
this.errorLog.push( this.errorLog.push(
'msvs_version does not match this VS Command Prompt or the', 'msvs_version does not match this VS Command Prompt or the',
@ -109,11 +111,11 @@ VisualStudioFinder.prototype = {
this.log.error(`\n${errorLog}\n\n${infoLog}\n`) this.log.error(`\n${errorLog}\n\n${infoLog}\n`)
throw 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 // Invoke the PowerShell script to get information about Visual Studio 2017
// or newer installations // or newer installations
findVisualStudio2017OrNewer: async function findVisualStudio2017OrNewer () { async findVisualStudio2017OrNewer () {
const ps = path.join(process.env.SystemRoot, 'System32', const ps = path.join(process.env.SystemRoot, 'System32',
'WindowsPowerShell', 'v1.0', 'powershell.exe') 'WindowsPowerShell', 'v1.0', 'powershell.exe')
const csFile = path.join(__dirname, 'Find-VisualStudio.cs') const csFile = path.join(__dirname, 'Find-VisualStudio.cs')
@ -128,11 +130,11 @@ VisualStudioFinder.prototype = {
this.log.silly('Running', ps, psArgs) this.log.silly('Running', ps, psArgs)
const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' }) const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' })
return this.parseData(err, stdout, stderr) return this.parseData(err, stdout, stderr)
}, }
// Parse the output of the PowerShell script and look for an installation // Parse the output of the PowerShell script and look for an installation
// of Visual Studio 2017 or newer to use // of Visual Studio 2017 or newer to use
parseData: function parseData (err, stdout, stderr) { parseData (err, stdout, stderr) {
this.log.silly('PS stderr = %j', stderr) this.log.silly('PS stderr = %j', stderr)
const failPowershell = () => { const failPowershell = () => {
@ -220,10 +222,10 @@ VisualStudioFinder.prototype = {
this.addLog( this.addLog(
'could not find a version of Visual Studio 2017 or newer to use') 'could not find a version of Visual Studio 2017 or newer to use')
return null return null
}, }
// Helper - process version information // Helper - process version information
getVersionInfo: function getVersionInfo (info) { getVersionInfo (info) {
const match = /^(\d+)\.(\d+)\..*/.exec(info.version) const match = /^(\d+)\.(\d+)\..*/.exec(info.version)
if (!match) { if (!match) {
this.log.silly('- failed to parse version:', info.version) this.log.silly('- failed to parse version:', info.version)
@ -249,14 +251,14 @@ VisualStudioFinder.prototype = {
} }
this.log.silly('- unsupported version:', ret.versionMajor) this.log.silly('- unsupported version:', ret.versionMajor)
return {} return {}
}, }
msBuildPathExists: function msBuildPathExists (path) { msBuildPathExists (path) {
return existsSync(path) return existsSync(path)
}, }
// Helper - process MSBuild information // Helper - process MSBuild information
getMSBuild: function getMSBuild (info, versionYear) { getMSBuild (info, versionYear) {
const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base'
const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe')
const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe') const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe')
@ -280,10 +282,10 @@ VisualStudioFinder.prototype = {
return msbuildPath return msbuildPath
} }
return null return null
}, }
// Helper - process toolset information // Helper - process toolset information
getToolset: function getToolset (info, versionYear) { getToolset (info, versionYear) {
const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' const pkg = 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64'
const express = 'Microsoft.VisualStudio.WDExpress' const express = 'Microsoft.VisualStudio.WDExpress'
@ -304,10 +306,10 @@ VisualStudioFinder.prototype = {
} }
this.log.silly('- invalid versionYear:', versionYear) this.log.silly('- invalid versionYear:', versionYear)
return null return null
}, }
// Helper - process Windows SDK information // Helper - process Windows SDK information
getSDK: function getSDK (info) { getSDK (info) {
const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK' const win8SDK = 'Microsoft.VisualStudio.Component.Windows81SDK'
const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.' const win10SDKPrefix = 'Microsoft.VisualStudio.Component.Windows10SDK.'
const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.' const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.'
@ -339,10 +341,10 @@ VisualStudioFinder.prototype = {
return '8.1' return '8.1'
} }
return null return null
}, }
// Find an installation of Visual Studio 2015 to use // Find an installation of Visual Studio 2015 to use
findVisualStudio2015: async function findVisualStudio2015 () { async findVisualStudio2015 () {
if (this.nodeSemver.major >= 19) { if (this.nodeSemver.major >= 19) {
this.addLog( this.addLog(
'not looking for VS2015 as it is only supported up to Node.js 18') 'not looking for VS2015 as it is only supported up to Node.js 18')
@ -355,10 +357,10 @@ VisualStudioFinder.prototype = {
versionYear: 2015, versionYear: 2015,
toolset: 'v140' toolset: 'v140'
}) })
}, }
// Find an installation of Visual Studio 2013 to use // Find an installation of Visual Studio 2013 to use
findVisualStudio2013: async function findVisualStudio2013 () { async findVisualStudio2013 () {
if (this.nodeSemver.major >= 9) { if (this.nodeSemver.major >= 9) {
this.addLog( this.addLog(
'not looking for VS2013 as it is only supported up to Node.js 8') 'not looking for VS2013 as it is only supported up to Node.js 8')
@ -371,10 +373,10 @@ VisualStudioFinder.prototype = {
versionYear: 2013, versionYear: 2013,
toolset: 'v120' toolset: 'v120'
}) })
}, }
// Helper - common code for VS2013 and VS2015 // Helper - common code for VS2013 and VS2015
findOldVS: async function findOldVS (info) { async findOldVS (info) {
const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7', const regVC7 = ['HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7',
'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7'] 'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7']
const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions' const regMSBuild = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions'
@ -408,14 +410,14 @@ VisualStudioFinder.prototype = {
this.addLog('- not found') this.addLog('- not found')
return null return null
} }
}, }
// After finding a usable version of Visual Studio: // After finding a usable version of Visual Studio:
// - add it to validVersions to be displayed at the end if a specific // - add it to validVersions to be displayed at the end if a specific
// version was requested and not found; // version was requested and not found;
// - check if this is the version that was requested. // - check if this is the version that was requested.
// - check if this matches the Visual Studio Command Prompt // - check if this matches the Visual Studio Command Prompt
checkConfigVersion: function checkConfigVersion (versionYear, vsPath) { checkConfigVersion (versionYear, vsPath) {
this.validVersions.push(versionYear) this.validVersions.push(versionYear)
this.validVersions.push(vsPath) this.validVersions.push(vsPath)
@ -438,10 +440,4 @@ VisualStudioFinder.prototype = {
} }
} }
const findVisualStudio = async (nodeSemver, configMsvsVersion) => new VisualStudioFinder(nodeSemver, configMsvsVersion).findVisualStudio() module.exports = VisualStudioFinder
module.exports = findVisualStudio
module.exports.test = {
VisualStudioFinder,
findVisualStudio
}

View file

@ -4,8 +4,8 @@ const path = require('path')
const nopt = require('nopt') const nopt = require('nopt')
const log = require('./log') const log = require('./log')
const childProcess = require('child_process') const childProcess = require('child_process')
const EE = require('events').EventEmitter const { EventEmitter } = require('events')
const { inherits } = require('util')
const commands = [ const commands = [
// Module build commands // Module build commands
'build', 'build',
@ -17,191 +17,172 @@ const commands = [
'list', 'list',
'remove' 'remove'
] ]
const aliases = {
ls: 'list',
rm: 'remove'
}
function gyp () { class Gyp extends EventEmitter {
return new Gyp() /**
} * Export the contents of the package.json.
*/
package = require('../package.json')
function Gyp () { /**
this.devDir = '' * nopt configuration definitions
*/
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'
'force-process-config': Boolean // 'configure'
}
this.commands = commands.reduce((acc, command) => { /**
acc[command] = (argv) => require('./' + command)(this, argv) * nopt shorthands
return acc */
}, {}) shorthands = {
} release: '--no-debug',
inherits(Gyp, EE) C: '--directory',
exports.Gyp = Gyp debug: '--debug',
const proto = Gyp.prototype j: '--jobs',
silly: '--loglevel=silly',
verbose: '--loglevel=verbose',
silent: '--loglevel=silent'
}
/** /**
* Export the contents of the package.json. * expose the command aliases for the bin file to use.
*/ */
aliases = {
ls: 'list',
rm: 'remove'
}
proto.package = require('../package.json') constructor (...args) {
super(...args)
/** this.devDir = ''
* nopt configuration definitions
*/
proto.configDefs = { this.commands = commands.reduce((acc, command) => {
help: Boolean, // everywhere acc[command] = (argv) => require('./' + command)(this, argv)
arch: String, // 'configure' return acc
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'
'force-process-config': Boolean // 'configure'
}
/** Object.defineProperty(this, 'version', {
* nopt shorthands enumerable: true,
*/ get: function () { return this.package.version }
})
}
proto.shorthands = { /**
release: '--no-debug', * Parses the given argv array and sets the 'opts',
C: '--directory', * 'argv' and 'command' properties.
debug: '--debug', */
j: '--jobs', parseArgv (argv) {
silly: '--loglevel=silly', this.opts = nopt(this.configDefs, this.shorthands, argv)
verbose: '--loglevel=verbose', this.argv = this.opts.argv.remain.slice()
silent: '--loglevel=silent'
}
/** const commands = this.todo = []
* expose the command aliases for the bin file to use.
*/
proto.aliases = aliases // create a copy of the argv array with aliases mapped
argv = this.argv.map((arg) => {
/**
* 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()
const commands = this.todo = []
// create a copy of the argv array with aliases mapped
argv = this.argv.map(function (arg) {
// is this an alias? // is this an alias?
if (arg in this.aliases) { if (arg in this.aliases) {
arg = this.aliases[arg] arg = this.aliases[arg]
}
return arg
}, this)
// process the mapped args into "command" objects ("name" and "args" props)
argv.slice().forEach(function (arg) {
if (arg in this.commands) {
const args = argv.splice(0, argv.indexOf(arg))
argv.shift()
if (commands.length > 0) {
commands[commands.length - 1].args = args
} }
commands.push({ name: arg, args: [] }) return arg
} })
}, this)
if (commands.length > 0) {
commands[commands.length - 1].args = argv.splice(0)
}
// support for inheriting config env variables from npm // process the mapped args into "command" objects ("name" and "args" props)
const npmConfigPrefix = 'npm_config_' argv.slice().forEach((arg) => {
Object.keys(process.env).forEach(function (name) { if (arg in this.commands) {
if (name.indexOf(npmConfigPrefix) !== 0) { const args = argv.splice(0, argv.indexOf(arg))
return argv.shift()
} if (commands.length > 0) {
const val = process.env[name] commands[commands.length - 1].args = args
if (name === npmConfigPrefix + 'loglevel') {
log.logger.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) {
// convert names like force_process_config to force-process-config
if (name.includes('_')) {
name = name.replace(/_/g, '-')
} }
this.opts[name] = val commands.push({ name: arg, args: [] })
} }
})
if (commands.length > 0) {
commands[commands.length - 1].args = argv.splice(0)
} }
}, this)
if (this.opts.loglevel) { // support for inheriting config env variables from npm
log.logger.level = this.opts.loglevel 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.logger.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) {
// convert names like force_process_config to force-process-config
if (name.includes('_')) {
name = name.replace(/_/g, '-')
}
this.opts[name] = val
}
}
})
if (this.opts.loglevel) {
log.logger.level = this.opts.loglevel
}
log.resume()
}
/**
* Spawns a child process and emits a 'spawn' event.
*/
spawn (command, args, opts) {
if (!opts) {
opts = {}
}
if (!opts.silent && !opts.stdio) {
opts.stdio = [0, 1, 2]
}
const 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 <command> [options]',
'',
' where <command> is one of:',
commands.map((c) => ' - ' + c + ' - ' + require('./' + c).usage).join('\n'),
'',
'node-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'),
'node@' + process.versions.node
].join('\n')
} }
log.resume()
} }
/** module.exports = () => new Gyp()
* Spawns a child process and emits a 'spawn' event. module.exports.Gyp = Gyp
*/
proto.spawn = function spawn (command, args, opts) {
if (!opts) {
opts = {}
}
if (!opts.silent && !opts.stdio) {
opts.stdio = [0, 1, 2]
}
const cp = childProcess.spawn(command, args, opts)
log.info('spawn', command)
log.info('spawn args', args)
return cp
}
/**
* Returns the usage instructions for node-gyp.
*/
proto.usage = function usage () {
const str = [
'',
' Usage: node-gyp <command> [options]',
'',
' where <command> 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
}
/**
* Version number getter.
*/
Object.defineProperty(proto, 'version', {
get: function () {
return this.package.version
},
enumerable: true
})
module.exports = exports = gyp

View file

@ -1,3 +1,17 @@
const envPaths = require('env-paths') const envPaths = require('env-paths')
module.exports.devDir = () => envPaths('node-gyp', { suffix: '' }).cache module.exports.devDir = () => envPaths('node-gyp', { suffix: '' }).cache
module.exports.poison = (object, property) => {
function fail () {
console.error(Error(`Property ${property} should not have been accessed.`))
process.abort()
}
const descriptor = {
configurable: false,
enumerable: false,
get: fail,
set: fail
}
Object.defineProperty(object, property, descriptor)
}

View file

@ -4,43 +4,34 @@ delete process.env.PYTHON
const { describe, it } = require('mocha') const { describe, it } = require('mocha')
const assert = require('assert') const assert = require('assert')
const { test: { PythonFinder, findPython: testFindPython } } = require('../lib/find-python') const PythonFinder = require('../lib/find-python')
const { execFile } = require('../lib/util') const { execFile } = require('../lib/util')
const { poison } = require('./common')
class TestPythonFinder extends PythonFinder {
constructor (...args) {
super(...args)
delete this.env.NODE_GYP_FORCE_PYTHON
}
async findPython () {
try {
return { err: null, python: await super.findPython() }
} catch (err) {
return { err, python: null }
}
}
}
describe('find-python', function () { describe('find-python', function () {
it('find python', async function () { it('find python', async function () {
const found = await testFindPython(null) const found = await PythonFinder.findPython(null)
const [err, stdout, stderr] = await execFile(found, ['-V'], { encoding: 'utf-8' }) const [err, stdout, stderr] = await execFile(found, ['-V'], { encoding: 'utf-8' })
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.ok(/Python 3/.test(stdout)) assert.ok(/Python 3/.test(stdout))
assert.strictEqual(stderr, '') assert.strictEqual(stderr, '')
}) })
function poison (object, property) {
function fail () {
console.error(Error(`Property ${property} should not have been accessed.`))
process.abort()
}
const descriptor = {
configurable: false,
enumerable: false,
get: fail,
set: fail
}
Object.defineProperty(object, property, descriptor)
}
function TestPythonFinder () { PythonFinder.apply(this, arguments) }
TestPythonFinder.prototype = Object.create(PythonFinder.prototype)
delete TestPythonFinder.prototype.env.NODE_GYP_FORCE_PYTHON
const findPython = async (f) => {
try {
return { err: null, python: await f.findPython() }
} catch (err) {
return { err, python: null }
}
}
it('find python - python', async function () { it('find python - python', async function () {
const f = new TestPythonFinder('python') const f = new TestPythonFinder('python')
f.execFile = async function (program, args, opts) { f.execFile = async function (program, args, opts) {
@ -55,7 +46,7 @@ describe('find-python', function () {
return [null, '/path/python'] return [null, '/path/python']
} }
const { err, python } = await findPython(f) const { err, python } = await f.findPython()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.strictEqual(python, '/path/python') assert.strictEqual(python, '/path/python')
}) })
@ -72,7 +63,7 @@ describe('find-python', function () {
} }
} }
const { err } = await findPython(f) const { err } = await f.findPython()
assert.ok(/Could not find any Python/.test(err)) assert.ok(/Could not find any Python/.test(err))
assert.ok(/not supported/i.test(f.errorLog)) assert.ok(/not supported/i.test(f.errorLog))
}) })
@ -89,7 +80,7 @@ describe('find-python', function () {
} }
} }
const { err } = await findPython(f) const { err } = await f.findPython()
assert.ok(/Could not find any Python/.test(err)) assert.ok(/Could not find any Python/.test(err))
assert.ok(/not in PATH/.test(f.errorLog)) assert.ok(/not in PATH/.test(f.errorLog))
}) })
@ -107,7 +98,7 @@ describe('find-python', function () {
} }
} }
const { err } = await findPython(f) const { err } = await f.findPython()
assert.ok(/Could not find any Python/.test(err)) assert.ok(/Could not find any Python/.test(err))
assert.ok(/not in PATH/.test(f.errorLog)) assert.ok(/not in PATH/.test(f.errorLog))
}) })
@ -136,7 +127,7 @@ describe('find-python', function () {
assert.fail() assert.fail()
} }
} }
const { err, python } = await findPython(f) const { err, python } = await f.findPython()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.strictEqual(python, 'Z:\\snake.exe') assert.strictEqual(python, 'Z:\\snake.exe')
}) })
@ -159,7 +150,7 @@ describe('find-python', function () {
assert.fail() assert.fail()
} }
} }
const { err, python } = await findPython(f) const { err, python } = await f.findPython()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.ok(python === expectedProgram) assert.ok(python === expectedProgram)
}) })
@ -177,7 +168,7 @@ describe('find-python', function () {
assert.fail() assert.fail()
} }
} }
const { err } = await findPython(f) const { err } = await f.findPython()
assert.ok(/Could not find any Python/.test(err)) assert.ok(/Could not find any Python/.test(err))
assert.ok(/not in PATH/.test(f.errorLog)) assert.ok(/not in PATH/.test(f.errorLog))
}) })

View file

@ -4,34 +4,20 @@ const { describe, it } = require('mocha')
const assert = require('assert') const assert = require('assert')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const { test: { VisualStudioFinder } } = require('../lib/find-visualstudio') const VisualStudioFinder = require('../lib/find-visualstudio')
const { poison } = require('./common')
const semverV1 = { major: 1, minor: 0, patch: 0 } const semverV1 = { major: 1, minor: 0, patch: 0 }
delete process.env.VCINSTALLDIR delete process.env.VCINSTALLDIR
function poison (object, property) { class TestVisualStudioFinder extends VisualStudioFinder {
function fail () { async findVisualStudio () {
console.error(Error(`Property ${property} should not have been accessed.`)) try {
process.abort() return { err: null, info: await super.findVisualStudio() }
} } catch (err) {
const descriptor = { return { err, info: null }
configurable: false, }
enumerable: false,
get: fail,
set: fail
}
Object.defineProperty(object, property, descriptor)
}
function TestVisualStudioFinder () { VisualStudioFinder.apply(this, arguments) }
TestVisualStudioFinder.prototype = Object.create(VisualStudioFinder.prototype)
const findVisualStudio = async (finder) => {
try {
return { err: null, info: await finder.findVisualStudio() }
} catch (err) {
return { err, info: null }
} }
} }
@ -62,7 +48,7 @@ describe('find-visualstudio', function () {
throw new Error() throw new Error()
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\MSBuild12\\MSBuild.exe', msBuild: 'C:\\MSBuild12\\MSBuild.exe',
@ -102,7 +88,7 @@ describe('find-visualstudio', function () {
throw new Error() throw new Error()
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.ok(/find .* Visual Studio/i.test(err), 'expect error') assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
assert.ok(!info, 'no data') assert.ok(!info, 'no data')
}) })
@ -129,7 +115,7 @@ describe('find-visualstudio', function () {
} }
throw new Error() throw new Error()
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\MSBuild14\\MSBuild.exe', msBuild: 'C:\\MSBuild14\\MSBuild.exe',
@ -228,7 +214,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
@ -254,7 +240,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
@ -279,7 +265,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\' +
@ -305,7 +291,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
@ -331,7 +317,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
@ -357,7 +343,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' + msBuild: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\' +
@ -392,7 +378,7 @@ describe('find-visualstudio', function () {
const data = fs.readFileSync(file) const data = fs.readFileSync(file)
return finder.parseData(null, data, '') return finder.parseData(null, data, '')
} }
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
msBuild: msBuildPath, msBuild: msBuildPath,
@ -456,7 +442,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, 'AABB') const finder = new TestVisualStudioFinder(semverV1, 'AABB')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.ok(/find .* Visual Studio/i.test(err), 'expect error') assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
assert.ok(!info, 'no data') assert.ok(!info, 'no data')
}) })
@ -465,7 +451,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, '2013') const finder = new TestVisualStudioFinder(semverV1, '2013')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2013) assert.deepStrictEqual(info.versionYear, 2013)
}) })
@ -474,7 +460,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013') const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2013')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, 'C:\\VS2013') assert.deepStrictEqual(info.path, 'C:\\VS2013')
}) })
@ -483,7 +469,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, '2015') const finder = new TestVisualStudioFinder(semverV1, '2015')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2015) assert.deepStrictEqual(info.versionYear, 2015)
}) })
@ -492,7 +478,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, 'C:\\VS2015') assert.deepStrictEqual(info.path, 'C:\\VS2015')
}) })
@ -501,7 +487,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, '2017') const finder = new TestVisualStudioFinder(semverV1, '2017')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2017) assert.deepStrictEqual(info.versionYear, 2017)
}) })
@ -511,7 +497,7 @@ describe('find-visualstudio', function () {
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, assert.deepStrictEqual(info.path,
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community')
@ -521,7 +507,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, '2019') const finder = new TestVisualStudioFinder(semverV1, '2019')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2019) assert.deepStrictEqual(info.versionYear, 2019)
}) })
@ -531,7 +517,7 @@ describe('find-visualstudio', function () {
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, assert.deepStrictEqual(info.path,
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
@ -545,7 +531,7 @@ describe('find-visualstudio', function () {
} }
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2022) assert.deepStrictEqual(info.versionYear, 2022)
}) })
@ -555,7 +541,7 @@ describe('find-visualstudio', function () {
'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS') 'c:\\program files (x86)\\microsoft visual studio\\2019\\BUILDTOOLS')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, assert.deepStrictEqual(info.path,
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
@ -569,7 +555,7 @@ describe('find-visualstudio', function () {
} }
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.versionYear, 2022) assert.deepStrictEqual(info.versionYear, 2022)
}) })
@ -582,7 +568,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, null) const finder = new TestVisualStudioFinder(semverV1, null)
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, 'C:\\VS2015') assert.deepStrictEqual(info.path, 'C:\\VS2015')
}) })
@ -594,7 +580,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, null) const finder = new TestVisualStudioFinder(semverV1, null)
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, assert.deepStrictEqual(info.path,
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools') 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools')
@ -607,7 +593,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, null) const finder = new TestVisualStudioFinder(semverV1, null)
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.ok(/find .* Visual Studio/i.test(err), 'expect error') assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
assert.ok(!info, 'no data') assert.ok(!info, 'no data')
}) })
@ -618,7 +604,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.strictEqual(err, null) assert.strictEqual(err, null)
assert.deepStrictEqual(info.path, 'C:\\VS2015') assert.deepStrictEqual(info.path, 'C:\\VS2015')
}) })
@ -630,7 +616,7 @@ describe('find-visualstudio', function () {
const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015') const finder = new TestVisualStudioFinder(semverV1, 'C:\\VS2015')
allVsVersions(finder) allVsVersions(finder)
const { err, info } = await findVisualStudio(finder) const { err, info } = await finder.findVisualStudio()
assert.ok(/find .* Visual Studio/i.test(err), 'expect error') assert.ok(/find .* Visual Studio/i.test(err), 'expect error')
assert.ok(!info, 'no data') assert.ok(!info, 'no data')
}) })