Skip to content

Commit

Permalink
feat: convert internal classes from util.inherits to classes
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
lukekarrys committed Oct 28, 2023
1 parent 355622f commit d52997e
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 340 deletions.
4 changes: 2 additions & 2 deletions lib/configure.js
Expand Up @@ -9,8 +9,8 @@ const win = process.platform === 'win32'
const findNodeDirectory = require('./find-node-directory')
const createConfigGypi = require('./create-config-gypi')
const { format: msgFormat } = require('util')
const findPython = require('./find-python')
const findVisualStudio = win ? require('./find-visualstudio') : null
const { findPython } = require('./find-python')
const { findVisualStudio } = win ? require('./find-visualstudio') : {}

async function configure (gyp, argv) {
const buildDir = path.resolve('build')
Expand Down
83 changes: 39 additions & 44 deletions lib/find-python.js
Expand Up @@ -2,10 +2,15 @@

const log = require('./log')
const semver = require('semver')
const { _extend: extend } = require('util') // eslint-disable-line n/no-deprecated-api
const { execFile } = require('./util')
const win = process.platform === 'win32'

function getOsUserInfo () {
try {
return require('os').userInfo().username
} catch {}
}

const systemDrive = process.env.SystemDrive || 'C:'
const username = process.env.USERNAME || process.env.USER || getOsUserInfo()
const localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local`
Expand All @@ -32,40 +37,36 @@ for (const majorMinor of ['311', '310', '39', '38']) {
}
}

function getOsUserInfo () {
try {
return require('os').userInfo().username
} catch (e) {}
}

function PythonFinder (configPython) {
this.configPython = configPython
this.errorLog = []
}
class PythonFinder {
static findPython = (...args) => new PythonFinder(...args).findPython()

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',
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:
execFile,
env: process.env,
win,
pyLauncher: 'py.exe',
winDefaultLocations: winDefaultLocationsArray,
execFile = execFile
env = process.env
win = win
pyLauncher = 'py.exe'
winDefaultLocations = winDefaultLocationsArray

constructor (configPython) {
this.configPython = configPython
this.errorLog = []
}

// 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: async function findPython () {
async findPython () {
const SKIP = 0
const FAIL = 1
const toCheck = (() => {
Expand Down Expand Up @@ -161,12 +162,12 @@ PythonFinder.prototype = {
}

return this.fail()
},
}

// Check if command is a valid Python to use.
// Will exit the Python finder on success.
// If on Windows, run in a CMD shell to support BAT/CMD launchers.
checkCommand: async function checkCommand (command) {
async checkCommand (command) {
let exec = command
let args = this.argsExecutable
let shell = false
Expand All @@ -190,7 +191,7 @@ PythonFinder.prototype = {
this.addLog(`- "${command}" is not in PATH or produced an error`)
throw err
}
},
}

// Check if the py launcher can find a valid Python to use.
// Will exit the Python finder on success.
Expand All @@ -202,7 +203,7 @@ PythonFinder.prototype = {
// the first command line argument. Since "py.exe -3" would be an invalid
// executable for "execFile", we have to use the launcher to figure out
// where the actual "python.exe" executable is located.
checkPyLauncher: async function checkPyLauncher () {
async checkPyLauncher () {
this.log.verbose(`- executing "${this.pyLauncher}" to get Python 3 executable path`)
// Possible outcomes: same as checkCommand
try {
Expand All @@ -213,11 +214,11 @@ PythonFinder.prototype = {
this.addLog(`- "${this.pyLauncher}" is not in PATH or produced an error`)
throw err
}
},
}

// Check if a Python executable is the correct version to use.
// Will exit the Python finder on success.
checkExecPath: async function checkExecPath (execPath) {
async checkExecPath (execPath) {
this.log.verbose(`- executing "${execPath}" to get version`)
// Possible outcomes:
// - Error: executable can not be run (likely meaning the command wasn't
Expand Down Expand Up @@ -249,11 +250,11 @@ PythonFinder.prototype = {
this.addLog(`- "${execPath}" could not be run`)
throw err
}
},
}

// Run an executable or shell command, trimming the output.
run: async function run (exec, args, shell) {
const env = extend({}, this.env)
async run (exec, args, shell) {
const env = Object.assign({}, this.env)
env.TERM = 'dumb'
const opts = { env, shell }

Expand All @@ -270,14 +271,14 @@ PythonFinder.prototype = {
this.log.silly('execFile: threw:\n%s', err.stack)
throw err
}
},
}

succeed: function succeed (execPath, version) {
succeed (execPath, version) {
this.log.info(`using Python version ${version} found at "${execPath}"`)
return execPath
},
}

fail: function fail () {
fail () {
const errorLog = this.errorLog.join('\n')

const pathExample = this.win
Expand Down Expand Up @@ -306,10 +307,4 @@ PythonFinder.prototype = {
}
}

const findPython = async (configPython) => new PythonFinder(configPython).findPython()

module.exports = findPython
module.exports.test = {
PythonFinder,
findPython
}
module.exports = PythonFinder
86 changes: 41 additions & 45 deletions lib/find-visualstudio.js
Expand Up @@ -5,26 +5,28 @@ const { existsSync } = require('fs')
const { win32: path } = require('path')
const { regSearchKeys, execFile } = require('./util')

function VisualStudioFinder (nodeSemver, configMsvsVersion) {
this.nodeSemver = nodeSemver
this.configMsvsVersion = configMsvsVersion
this.errorLog = []
this.validVersions = []
}
class VisualStudioFinder {
static findVisualStudio = (...args) => new VisualStudioFinder(...args).findVisualStudio()

log = log.withPrefix('find VS')

VisualStudioFinder.prototype = {
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
// 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: async function findVisualStudio () {
async findVisualStudio () {
this.configVersionYear = null
this.configPath = null
if (this.configMsvsVersion) {
Expand Down Expand Up @@ -65,16 +67,16 @@ VisualStudioFinder.prototype = {
}

return 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')
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',
Expand Down Expand Up @@ -109,11 +111,11 @@ VisualStudioFinder.prototype = {

this.log.error(`\n${errorLog}\n\n${infoLog}\n`)
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: async function findVisualStudio2017OrNewer () {
async findVisualStudio2017OrNewer () {
const ps = path.join(process.env.SystemRoot, 'System32',
'WindowsPowerShell', 'v1.0', 'powershell.exe')
const csFile = path.join(__dirname, 'Find-VisualStudio.cs')
Expand All @@ -128,11 +130,11 @@ VisualStudioFinder.prototype = {
this.log.silly('Running', ps, psArgs)
const [err, stdout, stderr] = await execFile(ps, psArgs, { encoding: 'utf8' })
return this.parseData(err, stdout, stderr)
},
}

// Parse the output of the PowerShell script and look for an installation
// of Visual Studio 2017 or newer to use
parseData: function parseData (err, stdout, stderr) {
parseData (err, stdout, stderr) {
this.log.silly('PS stderr = %j', stderr)

const failPowershell = () => {
Expand Down Expand Up @@ -220,10 +222,10 @@ VisualStudioFinder.prototype = {
this.addLog(
'could not find a version of Visual Studio 2017 or newer to use')
return null
},
}

// 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)
Expand All @@ -249,14 +251,14 @@ VisualStudioFinder.prototype = {
}
this.log.silly('- unsupported version:', ret.versionMajor)
return {}
},
}

msBuildPathExists: function msBuildPathExists (path) {
msBuildPathExists (path) {
return existsSync(path)
},
}

// Helper - process MSBuild information
getMSBuild: function getMSBuild (info, versionYear) {
getMSBuild (info, versionYear) {
const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base'
const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe')
const msbuildPathArm64 = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'arm64', 'MSBuild.exe')
Expand All @@ -280,10 +282,10 @@ VisualStudioFinder.prototype = {
return msbuildPath
}
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'

Expand All @@ -304,10 +306,10 @@ 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.'
const win11SDKPrefix = 'Microsoft.VisualStudio.Component.Windows11SDK.'
Expand Down Expand Up @@ -339,10 +341,10 @@ VisualStudioFinder.prototype = {
return '8.1'
}
return null
},
}

// Find an installation of Visual Studio 2015 to use
findVisualStudio2015: async function findVisualStudio2015 () {
async findVisualStudio2015 () {
if (this.nodeSemver.major >= 19) {
this.addLog(
'not looking for VS2015 as it is only supported up to Node.js 18')
Expand All @@ -355,10 +357,10 @@ VisualStudioFinder.prototype = {
versionYear: 2015,
toolset: 'v140'
})
},
}

// Find an installation of Visual Studio 2013 to use
findVisualStudio2013: async function findVisualStudio2013 () {
async findVisualStudio2013 () {
if (this.nodeSemver.major >= 9) {
this.addLog(
'not looking for VS2013 as it is only supported up to Node.js 8')
Expand All @@ -371,10 +373,10 @@ VisualStudioFinder.prototype = {
versionYear: 2013,
toolset: 'v120'
})
},
}

// Helper - common code for VS2013 and VS2015
findOldVS: async function findOldVS (info) {
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'
Expand Down Expand Up @@ -408,14 +410,14 @@ VisualStudioFinder.prototype = {
this.addLog('- not found')
return null
}
},
}

// After finding a usable version of Visual Studio:
// - 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)

Expand All @@ -438,10 +440,4 @@ VisualStudioFinder.prototype = {
}
}

const findVisualStudio = async (nodeSemver, configMsvsVersion) => new VisualStudioFinder(nodeSemver, configMsvsVersion).findVisualStudio()

module.exports = findVisualStudio
module.exports.test = {
VisualStudioFinder,
findVisualStudio
}
module.exports = VisualStudioFinder

0 comments on commit d52997e

Please sign in to comment.