Comparing version
@@ -16,2 +16,6 @@ <!-- | ||
Please look thru your error log for the string `gyp info using node-gyp@` and if the version number is less than the [current release of node-gyp](https://github.com/nodejs/node-gyp/releases) then __please upgrade__ using the instructions at https://github.com/nodejs/node-gyp/blob/master/docs/Updating-npm-bundled-node-gyp.md and try your command again. | ||
Requests for help with [`node-sass` are very common](https://github.com/nodejs/node-gyp/issues?q=label%3A%22Node+Sass+--%3E+Dart+Sass%22). Please be aware that this package is deprecated, you should seek alternatives and avoid opening new issues about it here. | ||
* **Node Version**: <!-- `node -v` and `npm -v` --> | ||
@@ -50,2 +54,1 @@ * **Platform**: <!-- `uname -a` (UNIX), or `systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /C:"System Type"` (Windows) --> | ||
<!-- Any further details --> | ||
@@ -13,3 +13,3 @@ <!-- | ||
- [ ] documentation is changed or added | ||
- [ ] commit message follows [commit guidelines](https://github.com/nodejs/node/blob/master/doc/guides/contributing/pull-requests.md#commit-message-guidelines) | ||
- [ ] commit message follows [commit guidelines](https://github.com/googleapis/release-please#how-should-i-write-my-commits) | ||
@@ -16,0 +16,0 @@ ##### Description of change |
# Changelog | ||
## [0.10.0](https://www.github.com/nodejs/gyp-next/compare/v0.9.6...v0.10.0) (2021-08-26) | ||
### Features | ||
* **msvs:** add support for Visual Studio 2022 ([#124](https://www.github.com/nodejs/gyp-next/issues/124)) ([4bd9215](https://www.github.com/nodejs/gyp-next/commit/4bd9215c44d300f06e916aec1d6327c22b78272d)) | ||
### [0.9.6](https://www.github.com/nodejs/gyp-next/compare/v0.9.5...v0.9.6) (2021-08-23) | ||
### Bug Fixes | ||
* align flake8 test ([#122](https://www.github.com/nodejs/gyp-next/issues/122)) ([f1faa8d](https://www.github.com/nodejs/gyp-next/commit/f1faa8d3081e1a47e917ff910892f00dff16cf8a)) | ||
* **msvs:** fix paths again in action command arguments ([#121](https://www.github.com/nodejs/gyp-next/issues/121)) ([7159dfb](https://www.github.com/nodejs/gyp-next/commit/7159dfbc5758c9ec717e215f2c36daf482c846a1)) | ||
### [0.9.5](https://www.github.com/nodejs/gyp-next/compare/v0.9.4...v0.9.5) (2021-08-18) | ||
### Bug Fixes | ||
* add python 3.6 to node-gyp integration test ([3462d4c](https://www.github.com/nodejs/gyp-next/commit/3462d4ce3c31cce747513dc7ca9760c81d57c60e)) | ||
* revert for windows compatibility ([d078e7d](https://www.github.com/nodejs/gyp-next/commit/d078e7d7ae080ddae243188f6415f940376a7368)) | ||
* support msvs_quote_cmd in ninja generator ([#117](https://www.github.com/nodejs/gyp-next/issues/117)) ([46486ac](https://www.github.com/nodejs/gyp-next/commit/46486ac6e9329529d51061e006a5b39631e46729)) | ||
### [0.9.4](https://www.github.com/nodejs/gyp-next/compare/v0.9.3...v0.9.4) (2021-08-09) | ||
### Bug Fixes | ||
* .S is an extension for asm file on Windows ([#115](https://www.github.com/nodejs/gyp-next/issues/115)) ([d2fad44](https://www.github.com/nodejs/gyp-next/commit/d2fad44ef3a79ca8900f1307060153ded57053fc)) | ||
### [0.9.3](https://www.github.com/nodejs/gyp-next/compare/v0.9.2...v0.9.3) (2021-07-07) | ||
### Bug Fixes | ||
* build failure with ninja and Python 3 on Windows ([#113](https://www.github.com/nodejs/gyp-next/issues/113)) ([c172d10](https://www.github.com/nodejs/gyp-next/commit/c172d105deff5db4244e583942215918fa80dd3c)) | ||
### [0.9.2](https://www.github.com/nodejs/gyp-next/compare/v0.9.1...v0.9.2) (2021-05-21) | ||
### Bug Fixes | ||
* add support of utf8 encoding ([#105](https://www.github.com/nodejs/gyp-next/issues/105)) ([4d0f93c](https://www.github.com/nodejs/gyp-next/commit/4d0f93c249286d1f0c0f665f5fe7346119f98cf1)) | ||
### [0.9.1](https://www.github.com/nodejs/gyp-next/compare/v0.9.0...v0.9.1) (2021-05-14) | ||
### Bug Fixes | ||
* py lint ([3b6a8ee](https://www.github.com/nodejs/gyp-next/commit/3b6a8ee7a66193a8a6867eba9e1d2b70bdf04402)) | ||
## [0.9.0](https://www.github.com/nodejs/gyp-next/compare/v0.8.1...v0.9.0) (2021-05-13) | ||
### Features | ||
* use LDFLAGS_host for host toolset ([#98](https://www.github.com/nodejs/gyp-next/issues/98)) ([bea5c7b](https://www.github.com/nodejs/gyp-next/commit/bea5c7bd67d6ad32acbdce79767a5481c70675a2)) | ||
### Bug Fixes | ||
* msvs.py: remove overindentation ([#102](https://www.github.com/nodejs/gyp-next/issues/102)) ([3f83e99](https://www.github.com/nodejs/gyp-next/commit/3f83e99056d004d9579ceb786e06b624ddc36529)) | ||
* update gyp.el to change case to cl-case ([#93](https://www.github.com/nodejs/gyp-next/issues/93)) ([13d5b66](https://www.github.com/nodejs/gyp-next/commit/13d5b66aab35985af9c2fb1174fdc6e1c1407ecc)) | ||
### [0.8.1](https://www.github.com/nodejs/gyp-next/compare/v0.8.0...v0.8.1) (2021-02-18) | ||
### Bug Fixes | ||
* update shebang lines from python to python3 ([#94](https://www.github.com/nodejs/gyp-next/issues/94)) ([a1b0d41](https://www.github.com/nodejs/gyp-next/commit/a1b0d4171a8049a4ab7a614202063dec332f2df4)) | ||
## [0.8.0](https://www.github.com/nodejs/gyp-next/compare/v0.7.0...v0.8.0) (2021-01-15) | ||
### ⚠ BREAKING CHANGES | ||
* remove support for Python 2 | ||
### Bug Fixes | ||
* revert posix build job ([#86](https://www.github.com/nodejs/gyp-next/issues/86)) ([39dc34f](https://www.github.com/nodejs/gyp-next/commit/39dc34f0799c074624005fb9bbccf6e028607f9d)) | ||
### gyp | ||
* Remove support for Python 2 ([#88](https://www.github.com/nodejs/gyp-next/issues/88)) ([22e4654](https://www.github.com/nodejs/gyp-next/commit/22e465426fd892403c95534229af819a99c3f8dc)) | ||
## [0.7.0](https://www.github.com/nodejs/gyp-next/compare/v0.6.2...v0.7.0) (2020-12-17) | ||
### ⚠ BREAKING CHANGES | ||
* **msvs:** On Windows, arguments passed to the "action" commands are no longer transformed to replace slashes with backslashes. | ||
### Features | ||
* **xcode:** --cross-compiling overrides arch-specific settings ([973bae0](https://www.github.com/nodejs/gyp-next/commit/973bae0b7b08be7b680ecae9565fbd04b3e0787d)) | ||
### Bug Fixes | ||
* **msvs:** do not fix paths in action command arguments ([fc22f83](https://www.github.com/nodejs/gyp-next/commit/fc22f8335e2016da4aae4f4233074bd651d2faea)) | ||
* cmake on python 3 ([fd61f5f](https://www.github.com/nodejs/gyp-next/commit/fd61f5faa5275ec8fc98e3c7868c0dd46f109540)) | ||
* ValueError: invalid mode: 'rU' while trying to load binding.gyp ([d0504e6](https://www.github.com/nodejs/gyp-next/commit/d0504e6700ce48f44957a4d5891b142a60be946f)) | ||
* xcode cmake parsing ([eefe8d1](https://www.github.com/nodejs/gyp-next/commit/eefe8d10e99863bc4ac7e2ed32facd608d400d4b)) | ||
### [0.6.2](https://www.github.com/nodejs/gyp-next/compare/v0.6.1...v0.6.2) (2020-10-16) | ||
@@ -4,0 +111,0 @@ |
# Code of Conduct | ||
* [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md) | ||
* [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/master/Moderation-Policy.md) | ||
* [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md) | ||
* [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/HEAD/Moderation-Policy.md) |
@@ -5,3 +5,3 @@ # Contributing to gyp-next | ||
This project is bound to the [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md). | ||
This project is bound to the [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md). | ||
@@ -8,0 +8,0 @@ <a id="developers-certificate-of-origin"></a> |
@@ -10,2 +10,3 @@ 'use strict' | ||
const findNodeDirectory = require('./find-node-directory') | ||
const createConfigGypi = require('./create-config-gypi') | ||
const msgFormat = require('util').format | ||
@@ -80,3 +81,5 @@ var findPython = require('./find-python') | ||
} | ||
log.verbose('build dir', '"build" dir needed to be created?', isNew) | ||
log.verbose( | ||
'build dir', '"build" dir needed to be created?', isNew ? 'Yes' : 'No' | ||
) | ||
if (win) { | ||
@@ -95,110 +98,15 @@ findVisualStudio(release.semver, gyp.opts.msvs_version, | ||
} | ||
var configFilename = 'config.gypi' | ||
var 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 | ||
// default "config.variables" | ||
if (!variables) { | ||
variables = config.variables = {} | ||
} | ||
// default "config.defaults" | ||
if (!defaults) { | ||
defaults = config.target_defaults = {} | ||
} | ||
// don't inherit the "defaults" from node's `process.config` object. | ||
// doing so could cause problems in cases where the `node` executable was | ||
// compiled on a different machine (with different lib/include paths) than | ||
// the machine where the addon is being built to | ||
defaults.cflags = [] | ||
defaults.defines = [] | ||
defaults.include_dirs = [] | ||
defaults.libraries = [] | ||
// set the default_configuration prop | ||
if ('debug' in gyp.opts) { | ||
defaults.default_configuration = gyp.opts.debug ? 'Debug' : 'Release' | ||
} | ||
if (!defaults.default_configuration) { | ||
defaults.default_configuration = 'Release' | ||
} | ||
// set the target_arch variable | ||
variables.target_arch = gyp.opts.arch || process.arch || 'ia32' | ||
if (variables.target_arch === 'arm64') { | ||
defaults.msvs_configuration_platform = 'ARM64' | ||
defaults.xcode_configuration_platform = 'arm64' | ||
} | ||
// set the node development directory | ||
variables.nodedir = nodeDir | ||
// disable -T "thin" static archives by default | ||
variables.standalone_static_library = gyp.opts.thin ? 0 : 1 | ||
if (win) { | ||
if (process.platform === 'win32') { | ||
process.env.GYP_MSVS_VERSION = Math.min(vsInfo.versionYear, 2015) | ||
process.env.GYP_MSVS_OVERRIDE_PATH = vsInfo.path | ||
defaults.msbuild_toolset = vsInfo.toolset | ||
if (vsInfo.sdk) { | ||
defaults.msvs_windows_target_platform_version = vsInfo.sdk | ||
} | ||
if (variables.target_arch === 'arm64') { | ||
if (vsInfo.versionMajor > 15 || | ||
(vsInfo.versionMajor === 15 && vsInfo.versionMajor >= 9)) { | ||
defaults.msvs_enable_marmasm = 1 | ||
} else { | ||
log.warn('Compiling ARM64 assembly is only available in\n' + | ||
'Visual Studio 2017 version 15.9 and above') | ||
} | ||
} | ||
variables.msbuild_path = vsInfo.msBuild | ||
} | ||
// loop through the rest of the opts and add the unknown ones as variables. | ||
// this allows for module-specific configure flags like: | ||
// | ||
// $ node-gyp configure --shared-libxml2 | ||
Object.keys(gyp.opts).forEach(function (opt) { | ||
if (opt === 'argv') { | ||
return | ||
} | ||
if (opt in gyp.configDefs) { | ||
return | ||
} | ||
variables[opt.replace(/-/g, '_')] = gyp.opts[opt] | ||
createConfigGypi({ gyp, buildDir, nodeDir, vsInfo }).then(configPath => { | ||
configs.push(configPath) | ||
findConfigs() | ||
}).catch(err => { | ||
callback(err) | ||
}) | ||
// 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' | ||
var 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) | ||
} | ||
function findConfigs (err) { | ||
if (err) { | ||
return callback(err) | ||
} | ||
function findConfigs () { | ||
var name = configNames.shift() | ||
@@ -205,0 +113,0 @@ if (!name) { |
'use strict' | ||
const path = require('path') | ||
const log = require('npmlog') | ||
@@ -11,2 +10,34 @@ const semver = require('semver') | ||
const systemDrive = process.env.SystemDrive || 'C:' | ||
const username = process.env.USERNAME || process.env.USER || getOsUserInfo() | ||
const localAppData = process.env.LOCALAPPDATA || `${systemDrive}\\${username}\\AppData\\Local` | ||
const foundLocalAppData = process.env.LOCALAPPDATA || username | ||
const programFiles = process.env.ProgramW6432 || process.env.ProgramFiles || `${systemDrive}\\Program Files` | ||
const programFilesX86 = process.env['ProgramFiles(x86)'] || `${programFiles} (x86)` | ||
const winDefaultLocationsArray = [] | ||
for (const majorMinor of ['39', '38', '37', '36']) { | ||
if (foundLocalAppData) { | ||
winDefaultLocationsArray.push( | ||
`${localAppData}\\Programs\\Python\\Python${majorMinor}\\python.exe`, | ||
`${programFiles}\\Python${majorMinor}\\python.exe`, | ||
`${localAppData}\\Programs\\Python\\Python${majorMinor}-32\\python.exe`, | ||
`${programFiles}\\Python${majorMinor}-32\\python.exe`, | ||
`${programFilesX86}\\Python${majorMinor}-32\\python.exe` | ||
) | ||
} else { | ||
winDefaultLocationsArray.push( | ||
`${programFiles}\\Python${majorMinor}\\python.exe`, | ||
`${programFiles}\\Python${majorMinor}-32\\python.exe`, | ||
`${programFilesX86}\\Python${majorMinor}-32\\python.exe` | ||
) | ||
} | ||
} | ||
function getOsUserInfo () { | ||
try { | ||
return require('os').userInfo().username | ||
} catch (e) {} | ||
} | ||
function PythonFinder (configPython, callback) { | ||
@@ -22,3 +53,3 @@ this.callback = callback | ||
argsVersion: ['-c', 'import sys; print("%s.%s.%s" % sys.version_info[:3]);'], | ||
semverRange: '2.7.x || >=3.5.0', | ||
semverRange: '>=3.6.0', | ||
@@ -30,6 +61,3 @@ // These can be overridden for testing: | ||
pyLauncher: 'py.exe', | ||
winDefaultLocations: [ | ||
path.join(process.env.SystemDrive || 'C:', 'Python37', 'python.exe'), | ||
path.join(process.env.SystemDrive || 'C:', 'Python27', 'python.exe') | ||
], | ||
winDefaultLocations: winDefaultLocationsArray, | ||
@@ -102,7 +130,2 @@ // Logs a message at verbose level, but also saves it to be displayed later | ||
arg: 'python' | ||
}, | ||
{ | ||
before: () => { this.addLog('checking if "python2" can be used') }, | ||
check: this.checkCommand, | ||
arg: 'python2' | ||
} | ||
@@ -126,3 +149,3 @@ ] | ||
this.addLog( | ||
'checking if the py launcher can be used to find Python') | ||
'checking if the py launcher can be used to find Python 3') | ||
}, | ||
@@ -196,6 +219,11 @@ check: this.checkPyLauncher | ||
// being in the $PATH. | ||
// Because the Python launcher supports Python 2 and Python 3, we should | ||
// explicitly request a Python 3 version. This is done by supplying "-3" as | ||
// the first command line argument. Since "py.exe -3" would be an invalid | ||
// executable for "execFile", we have to use the launcher to figure out | ||
// where the actual "python.exe" executable is located. | ||
checkPyLauncher: function checkPyLauncher (errorCallback) { | ||
this.log.verbose( | ||
`- executing "${this.pyLauncher}" to get Python executable path`) | ||
this.run(this.pyLauncher, this.argsExecutable, false, | ||
`- executing "${this.pyLauncher}" to get Python 3 executable path`) | ||
this.run(this.pyLauncher, ['-3', ...this.argsExecutable], false, | ||
function (err, execPath) { | ||
@@ -202,0 +230,0 @@ // Possible outcomes: same as checkCommand |
@@ -5,2 +5,3 @@ 'use strict' | ||
const execFile = require('child_process').execFile | ||
const fs = require('fs') | ||
const path = require('path').win32 | ||
@@ -261,2 +262,6 @@ const logWithPrefix = require('./util').logWithPrefix | ||
} | ||
if (ret.versionMajor === 17) { | ||
ret.versionYear = 2022 | ||
return ret | ||
} | ||
this.log.silly('- unsupported version:', ret.versionMajor) | ||
@@ -269,2 +274,3 @@ return {} | ||
const pkg = 'Microsoft.VisualStudio.VC.MSBuild.Base' | ||
const msbuildPath = path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') | ||
if (info.packages.indexOf(pkg) !== -1) { | ||
@@ -276,5 +282,9 @@ this.log.silly('- found VC.MSBuild.Base') | ||
if (versionYear === 2019) { | ||
return path.join(info.path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') | ||
return msbuildPath | ||
} | ||
} | ||
// visual studio 2022 don't has msbuild pkg | ||
if (fs.existsSync(msbuildPath)) { | ||
return msbuildPath | ||
} | ||
return null | ||
@@ -300,2 +310,4 @@ }, | ||
return 'v142' | ||
} else if (versionYear === 2022) { | ||
return 'v143' | ||
} | ||
@@ -407,3 +419,3 @@ this.log.silly('- invalid versionYear:', versionYear) | ||
// After finding a usable version of Visual Stuido: | ||
// After finding a usable version of Visual Studio: | ||
// - add it to validVersions to be displayed at the end if a specific | ||
@@ -410,0 +422,0 @@ // version was requested and not found; |
@@ -7,29 +7,18 @@ 'use strict' | ||
const path = require('path') | ||
const util = require('util') | ||
const stream = require('stream') | ||
const crypto = require('crypto') | ||
const log = require('npmlog') | ||
const semver = require('semver') | ||
const request = require('request') | ||
const fetch = require('make-fetch-happen') | ||
const processRelease = require('./process-release') | ||
const win = process.platform === 'win32' | ||
const getProxyFromURI = require('./proxy') | ||
const streamPipeline = util.promisify(stream.pipeline) | ||
function install (fs, gyp, argv, callback) { | ||
var release = processRelease(argv, gyp, process.version, process.release) | ||
/** | ||
* @param {typeof import('graceful-fs')} fs | ||
*/ | ||
// ensure no double-callbacks happen | ||
function cb (err) { | ||
if (cb.done) { | ||
return | ||
} | ||
cb.done = true | ||
if (err) { | ||
log.warn('install', 'got an error, rolling back install') | ||
// roll-back the install if anything went wrong | ||
gyp.commands.remove([release.versionDir], function () { | ||
callback(err) | ||
}) | ||
} else { | ||
callback(null, release.version) | ||
} | ||
} | ||
async function install (fs, gyp, argv) { | ||
const release = processRelease(argv, gyp, process.version, process.release) | ||
@@ -41,7 +30,7 @@ // Determine which node dev files version we are installing | ||
// could not parse the version string with semver | ||
return callback(new Error('Invalid version number: ' + release.version)) | ||
throw new Error('Invalid version number: ' + release.version) | ||
} | ||
if (semver.lt(release.version, '0.8.0')) { | ||
return callback(new Error('Minimum target version is `0.8.0` or greater. Got: ' + release.version)) | ||
throw new Error('Minimum target version is `0.8.0` or greater. Got: ' + release.version) | ||
} | ||
@@ -52,8 +41,6 @@ | ||
log.verbose('detected "pre" node version', release.version) | ||
if (gyp.opts.nodedir) { | ||
log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir) | ||
callback() | ||
} else { | ||
callback(new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead')) | ||
if (!gyp.opts.nodedir) { | ||
throw new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead') | ||
} | ||
log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir) | ||
return | ||
@@ -66,3 +53,3 @@ } | ||
// the directory where the dev files will be installed | ||
var devDir = path.resolve(gyp.devDir, release.versionDir) | ||
const devDir = path.resolve(gyp.devDir, release.versionDir) | ||
@@ -73,58 +60,53 @@ // If '--ensure' was passed, then don't *always* install the version; | ||
log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed') | ||
fs.stat(devDir, function (err) { | ||
if (err) { | ||
if (err.code === 'ENOENT') { | ||
log.verbose('install', 'version not already installed, continuing with install', release.version) | ||
go() | ||
} else if (err.code === 'EACCES') { | ||
eaccesFallback(err) | ||
} else { | ||
cb(err) | ||
try { | ||
await fs.promises.stat(devDir) | ||
} catch (err) { | ||
if (err.code === 'ENOENT') { | ||
log.verbose('install', 'version not already installed, continuing with install', release.version) | ||
try { | ||
return await go() | ||
} catch (err) { | ||
return rollback(err) | ||
} | ||
return | ||
} else if (err.code === 'EACCES') { | ||
return eaccesFallback(err) | ||
} | ||
log.verbose('install', 'version is already installed, need to check "installVersion"') | ||
var installVersionFile = path.resolve(devDir, 'installVersion') | ||
fs.readFile(installVersionFile, 'ascii', function (err, ver) { | ||
if (err && err.code !== 'ENOENT') { | ||
return cb(err) | ||
} | ||
var installVersion = parseInt(ver, 10) || 0 | ||
log.verbose('got "installVersion"', installVersion) | ||
log.verbose('needs "installVersion"', gyp.package.installVersion) | ||
if (installVersion < gyp.package.installVersion) { | ||
log.verbose('install', 'version is no good; reinstalling') | ||
go() | ||
} else { | ||
log.verbose('install', 'version is good') | ||
cb() | ||
} | ||
}) | ||
}) | ||
throw err | ||
} | ||
log.verbose('install', 'version is already installed, need to check "installVersion"') | ||
const installVersionFile = path.resolve(devDir, 'installVersion') | ||
let installVersion = 0 | ||
try { | ||
const ver = await fs.promises.readFile(installVersionFile, 'ascii') | ||
installVersion = parseInt(ver, 10) || 0 | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
throw err | ||
} | ||
} | ||
log.verbose('got "installVersion"', installVersion) | ||
log.verbose('needs "installVersion"', gyp.package.installVersion) | ||
if (installVersion < gyp.package.installVersion) { | ||
log.verbose('install', 'version is no good; reinstalling') | ||
try { | ||
return await go() | ||
} catch (err) { | ||
return rollback(err) | ||
} | ||
} | ||
log.verbose('install', 'version is good') | ||
} else { | ||
go() | ||
try { | ||
return await go() | ||
} catch (err) { | ||
return rollback(err) | ||
} | ||
} | ||
function getContentSha (res, callback) { | ||
var shasum = crypto.createHash('sha256') | ||
res.on('data', function (chunk) { | ||
shasum.update(chunk) | ||
}).on('end', function () { | ||
callback(null, shasum.digest('hex')) | ||
}) | ||
} | ||
function go () { | ||
async function go () { | ||
log.verbose('ensuring nodedir is created', devDir) | ||
// first create the dir for the node dev files | ||
fs.mkdir(devDir, { recursive: true }, function (err, created) { | ||
if (err) { | ||
if (err.code === 'EACCES') { | ||
eaccesFallback(err) | ||
} else { | ||
cb(err) | ||
} | ||
return | ||
} | ||
try { | ||
const created = await fs.promises.mkdir(devDir, { recursive: true }) | ||
@@ -134,226 +116,160 @@ if (created) { | ||
} | ||
} catch (err) { | ||
if (err.code === 'EACCES') { | ||
return eaccesFallback(err) | ||
} | ||
// now download the node tarball | ||
var tarPath = gyp.opts.tarball | ||
var badDownload = false | ||
var extractCount = 0 | ||
var contentShasums = {} | ||
var expectShasums = {} | ||
throw err | ||
} | ||
// checks if a file to be extracted from the tarball is valid. | ||
// only .h header files and the gyp files get extracted | ||
function isValid (path) { | ||
var isValid = valid(path) | ||
if (isValid) { | ||
log.verbose('extracted file from tarball', path) | ||
extractCount++ | ||
} else { | ||
// invalid | ||
log.silly('ignoring from tarball', path) | ||
} | ||
return isValid | ||
} | ||
// now download the node tarball | ||
const tarPath = gyp.opts.tarball | ||
let extractCount = 0 | ||
const contentShasums = {} | ||
const expectShasums = {} | ||
// download the tarball and extract! | ||
if (tarPath) { | ||
return tar.extract({ | ||
file: tarPath, | ||
strip: 1, | ||
filter: isValid, | ||
cwd: devDir | ||
}).then(afterTarball, cb) | ||
// checks if a file to be extracted from the tarball is valid. | ||
// only .h header files and the gyp files get extracted | ||
function isValid (path) { | ||
const isValid = valid(path) | ||
if (isValid) { | ||
log.verbose('extracted file from tarball', path) | ||
extractCount++ | ||
} else { | ||
// invalid | ||
log.silly('ignoring from tarball', path) | ||
} | ||
return isValid | ||
} | ||
try { | ||
var req = download(gyp, process.env, release.tarballUrl) | ||
} catch (e) { | ||
return cb(e) | ||
} | ||
// download the tarball and extract! | ||
// something went wrong downloading the tarball? | ||
req.on('error', function (err) { | ||
if (err.code === 'ENOTFOUND') { | ||
return cb(new Error('This is most likely not a problem with node-gyp or the package itself and\n' + | ||
'is related to network connectivity. In most cases you are behind a proxy or have bad \n' + | ||
'network settings.')) | ||
} | ||
badDownload = true | ||
cb(err) | ||
if (tarPath) { | ||
await tar.extract({ | ||
file: tarPath, | ||
strip: 1, | ||
filter: isValid, | ||
cwd: devDir | ||
}) | ||
} else { | ||
try { | ||
const res = await download(gyp, release.tarballUrl) | ||
req.on('close', function () { | ||
if (extractCount === 0) { | ||
cb(new Error('Connection closed while downloading tarball file')) | ||
if (res.status !== 200) { | ||
throw new Error(`${res.status} response downloading ${release.tarballUrl}`) | ||
} | ||
}) | ||
req.on('response', function (res) { | ||
if (res.statusCode !== 200) { | ||
badDownload = true | ||
cb(new Error(res.statusCode + ' response downloading ' + release.tarballUrl)) | ||
return | ||
await streamPipeline( | ||
res.body, | ||
// content checksum | ||
new ShaSum((_, checksum) => { | ||
const filename = path.basename(release.tarballUrl).trim() | ||
contentShasums[filename] = checksum | ||
log.verbose('content checksum', filename, checksum) | ||
}), | ||
tar.extract({ | ||
strip: 1, | ||
cwd: devDir, | ||
filter: isValid | ||
}) | ||
) | ||
} catch (err) { | ||
// something went wrong downloading the tarball? | ||
if (err.code === 'ENOTFOUND') { | ||
throw new Error('This is most likely not a problem with node-gyp or the package itself and\n' + | ||
'is related to network connectivity. In most cases you are behind a proxy or have bad \n' + | ||
'network settings.') | ||
} | ||
// content checksum | ||
getContentSha(res, function (_, checksum) { | ||
var filename = path.basename(release.tarballUrl).trim() | ||
contentShasums[filename] = checksum | ||
log.verbose('content checksum', filename, checksum) | ||
}) | ||
throw err | ||
} | ||
} | ||
// start unzipping and untaring | ||
res.pipe(tar.extract({ | ||
strip: 1, | ||
cwd: devDir, | ||
filter: isValid | ||
}).on('close', afterTarball).on('error', cb)) | ||
}) | ||
// invoked after the tarball has finished being extracted | ||
if (extractCount === 0) { | ||
throw new Error('There was a fatal problem while downloading/extracting the tarball') | ||
} | ||
// invoked after the tarball has finished being extracted | ||
function afterTarball () { | ||
if (badDownload) { | ||
return | ||
} | ||
if (extractCount === 0) { | ||
return cb(new Error('There was a fatal problem while downloading/extracting the tarball')) | ||
} | ||
log.verbose('tarball', 'done parsing tarball') | ||
var async = 0 | ||
log.verbose('tarball', 'done parsing tarball') | ||
if (win) { | ||
// need to download node.lib | ||
async++ | ||
downloadNodeLib(deref) | ||
} | ||
const installVersionPath = path.resolve(devDir, 'installVersion') | ||
await Promise.all([ | ||
// need to download node.lib | ||
...(win ? downloadNodeLib() : []), | ||
// write the "installVersion" file | ||
fs.promises.writeFile(installVersionPath, gyp.package.installVersion + '\n'), | ||
// Only download SHASUMS.txt if we downloaded something in need of SHA verification | ||
...(!tarPath || win ? [downloadShasums()] : []) | ||
]) | ||
// write the "installVersion" file | ||
async++ | ||
var installVersionPath = path.resolve(devDir, 'installVersion') | ||
fs.writeFile(installVersionPath, gyp.package.installVersion + '\n', deref) | ||
log.verbose('download contents checksum', JSON.stringify(contentShasums)) | ||
// check content shasums | ||
for (const k in contentShasums) { | ||
log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k]) | ||
if (contentShasums[k] !== expectShasums[k]) { | ||
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k]) | ||
} | ||
} | ||
// Only download SHASUMS.txt if we downloaded something in need of SHA verification | ||
if (!tarPath || win) { | ||
// download SHASUMS.txt | ||
async++ | ||
downloadShasums(deref) | ||
} | ||
async function downloadShasums () { | ||
log.verbose('check download content checksum, need to download `SHASUMS256.txt`...') | ||
log.verbose('checksum url', release.shasumsUrl) | ||
if (async === 0) { | ||
// no async tasks required | ||
cb() | ||
} | ||
const res = await download(gyp, release.shasumsUrl) | ||
function deref (err) { | ||
if (err) { | ||
return cb(err) | ||
} | ||
async-- | ||
if (!async) { | ||
log.verbose('download contents checksum', JSON.stringify(contentShasums)) | ||
// check content shasums | ||
for (var k in contentShasums) { | ||
log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k]) | ||
if (contentShasums[k] !== expectShasums[k]) { | ||
cb(new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])) | ||
return | ||
} | ||
} | ||
cb() | ||
} | ||
} | ||
if (res.status !== 200) { | ||
throw new Error(`${res.status} status code downloading checksum`) | ||
} | ||
function downloadShasums (done) { | ||
log.verbose('check download content checksum, need to download `SHASUMS256.txt`...') | ||
log.verbose('checksum url', release.shasumsUrl) | ||
try { | ||
var req = download(gyp, process.env, release.shasumsUrl) | ||
} catch (e) { | ||
return cb(e) | ||
for (const line of (await res.text()).trim().split('\n')) { | ||
const items = line.trim().split(/\s+/) | ||
if (items.length !== 2) { | ||
return | ||
} | ||
req.on('error', done) | ||
req.on('response', function (res) { | ||
if (res.statusCode !== 200) { | ||
done(new Error(res.statusCode + ' status code downloading checksum')) | ||
return | ||
} | ||
var chunks = [] | ||
res.on('data', function (chunk) { | ||
chunks.push(chunk) | ||
}) | ||
res.on('end', function () { | ||
var lines = Buffer.concat(chunks).toString().trim().split('\n') | ||
lines.forEach(function (line) { | ||
var items = line.trim().split(/\s+/) | ||
if (items.length !== 2) { | ||
return | ||
} | ||
// 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz | ||
var name = items[1].replace(/^\.\//, '') | ||
expectShasums[name] = items[0] | ||
}) | ||
log.verbose('checksum data', JSON.stringify(expectShasums)) | ||
done() | ||
}) | ||
}) | ||
// 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz | ||
const name = items[1].replace(/^\.\//, '') | ||
expectShasums[name] = items[0] | ||
} | ||
function downloadNodeLib (done) { | ||
log.verbose('on Windows; need to download `' + release.name + '.lib`...') | ||
var archs = ['ia32', 'x64', 'arm64'] | ||
var async = archs.length | ||
archs.forEach(function (arch) { | ||
var dir = path.resolve(devDir, arch) | ||
var targetLibPath = path.resolve(dir, release.name + '.lib') | ||
var libUrl = release[arch].libUrl | ||
var libPath = release[arch].libPath | ||
var name = arch + ' ' + release.name + '.lib' | ||
log.verbose(name, 'dir', dir) | ||
log.verbose(name, 'url', libUrl) | ||
log.verbose('checksum data', JSON.stringify(expectShasums)) | ||
} | ||
fs.mkdir(dir, { recursive: true }, function (err) { | ||
if (err) { | ||
return done(err) | ||
} | ||
log.verbose('streaming', name, 'to:', targetLibPath) | ||
function downloadNodeLib () { | ||
log.verbose('on Windows; need to download `' + release.name + '.lib`...') | ||
const archs = ['ia32', 'x64', 'arm64'] | ||
return archs.map(async (arch) => { | ||
const dir = path.resolve(devDir, arch) | ||
const targetLibPath = path.resolve(dir, release.name + '.lib') | ||
const { libUrl, libPath } = release[arch] | ||
const name = `${arch} ${release.name}.lib` | ||
log.verbose(name, 'dir', dir) | ||
log.verbose(name, 'url', libUrl) | ||
try { | ||
var req = download(gyp, process.env, libUrl, cb) | ||
} catch (e) { | ||
return cb(e) | ||
} | ||
await fs.promises.mkdir(dir, { recursive: true }) | ||
log.verbose('streaming', name, 'to:', targetLibPath) | ||
req.on('error', done) | ||
req.on('response', function (res) { | ||
if (res.statusCode === 403 || res.statusCode === 404) { | ||
if (arch === 'arm64') { | ||
// Arm64 is a newer platform on Windows and not all node distributions provide it. | ||
log.verbose(`${name} was not found in ${libUrl}`) | ||
} else { | ||
log.warn(`${name} was not found in ${libUrl}`) | ||
} | ||
return | ||
} else if (res.statusCode !== 200) { | ||
done(new Error(res.statusCode + ' status code downloading ' + name)) | ||
return | ||
} | ||
const res = await download(gyp, libUrl) | ||
getContentSha(res, function (_, checksum) { | ||
contentShasums[libPath] = checksum | ||
log.verbose('content checksum', libPath, checksum) | ||
}) | ||
if (res.status === 403 || res.status === 404) { | ||
if (arch === 'arm64') { | ||
// Arm64 is a newer platform on Windows and not all node distributions provide it. | ||
log.verbose(`${name} was not found in ${libUrl}`) | ||
} else { | ||
log.warn(`${name} was not found in ${libUrl}`) | ||
} | ||
return | ||
} else if (res.status !== 200) { | ||
throw new Error(`${res.status} status code downloading ${name}`) | ||
} | ||
var ws = fs.createWriteStream(targetLibPath) | ||
ws.on('error', cb) | ||
req.pipe(ws) | ||
}) | ||
req.on('end', function () { --async || done() }) | ||
}) | ||
}) | ||
} // downloadNodeLib() | ||
}) // mkdir() | ||
return streamPipeline( | ||
res.body, | ||
new ShaSum((_, checksum) => { | ||
contentShasums[libPath] = checksum | ||
log.verbose('content checksum', libPath, checksum) | ||
}), | ||
fs.createWriteStream(targetLibPath) | ||
) | ||
}) | ||
} // downloadNodeLib() | ||
} // go() | ||
@@ -367,6 +283,13 @@ | ||
// header files | ||
var extname = path.extname(file) | ||
const extname = path.extname(file) | ||
return extname === '.h' || extname === '.gypi' | ||
} | ||
async function rollback (err) { | ||
log.warn('install', 'got an error, rolling back install') | ||
// roll-back the install if anything went wrong | ||
await util.promisify(gyp.commands.remove)([release.versionDir]) | ||
throw err | ||
} | ||
/** | ||
@@ -381,10 +304,10 @@ * The EACCES fallback is a workaround for npm's `sudo` behavior, where | ||
function eaccesFallback (err) { | ||
var noretry = '--node_gyp_internal_noretry' | ||
async function eaccesFallback (err) { | ||
const noretry = '--node_gyp_internal_noretry' | ||
if (argv.indexOf(noretry) !== -1) { | ||
return cb(err) | ||
throw err | ||
} | ||
var tmpdir = os.tmpdir() | ||
const tmpdir = os.tmpdir() | ||
gyp.devDir = path.resolve(tmpdir, '.node-gyp') | ||
var userString = '' | ||
let userString = '' | ||
try { | ||
@@ -400,46 +323,52 @@ // os.userInfo can fail on some systems, it's not critical here | ||
} | ||
gyp.commands.install([noretry].concat(argv), cb) | ||
return util.promisify(gyp.commands.install)([noretry].concat(argv)) | ||
} | ||
} | ||
function download (gyp, env, url) { | ||
class ShaSum extends stream.Transform { | ||
constructor (callback) { | ||
super() | ||
this._callback = callback | ||
this._digester = crypto.createHash('sha256') | ||
} | ||
_transform (chunk, _, callback) { | ||
this._digester.update(chunk) | ||
callback(null, chunk) | ||
} | ||
_flush (callback) { | ||
this._callback(null, this._digester.digest('hex')) | ||
callback() | ||
} | ||
} | ||
async function download (gyp, url) { | ||
log.http('GET', url) | ||
var requestOpts = { | ||
uri: url, | ||
const requestOpts = { | ||
headers: { | ||
'User-Agent': 'node-gyp v' + gyp.version + ' (node ' + process.version + ')', | ||
'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`, | ||
Connection: 'keep-alive' | ||
} | ||
}, | ||
proxy: gyp.opts.proxy, | ||
noProxy: gyp.opts.noproxy | ||
} | ||
var cafile = gyp.opts.cafile | ||
const cafile = gyp.opts.cafile | ||
if (cafile) { | ||
requestOpts.ca = readCAFile(cafile) | ||
requestOpts.ca = await readCAFile(cafile) | ||
} | ||
// basic support for a proxy server | ||
var proxyUrl = getProxyFromURI(gyp, env, url) | ||
if (proxyUrl) { | ||
if (/^https?:\/\//i.test(proxyUrl)) { | ||
log.verbose('download', 'using proxy url: "%s"', proxyUrl) | ||
requestOpts.proxy = proxyUrl | ||
} else { | ||
log.warn('download', 'ignoring invalid "proxy" config setting: "%s"', proxyUrl) | ||
} | ||
} | ||
const res = await fetch(url, requestOpts) | ||
log.http(res.status, res.url) | ||
var req = request(requestOpts) | ||
req.on('response', function (res) { | ||
log.http(res.statusCode, url) | ||
}) | ||
return req | ||
return res | ||
} | ||
function readCAFile (filename) { | ||
async function readCAFile (filename) { | ||
// The CA file can contain multiple certificates so split on certificate | ||
// boundaries. [\S\s]*? is used to match everything including newlines. | ||
var ca = fs.readFileSync(filename, 'utf8') | ||
var re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g | ||
const ca = await fs.promises.readFile(filename, 'utf8') | ||
const re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g | ||
return ca.match(re) | ||
@@ -449,9 +378,9 @@ } | ||
module.exports = function (gyp, argv, callback) { | ||
return install(fs, gyp, argv, callback) | ||
install(fs, gyp, argv).then(callback.bind(undefined, null), callback) | ||
} | ||
module.exports.test = { | ||
download: download, | ||
install: install, | ||
readCAFile: readCAFile | ||
download, | ||
install, | ||
readCAFile | ||
} | ||
module.exports.usage = 'Install node development files for the specified node version.' |
@@ -78,3 +78,4 @@ 'use strict' | ||
jobs: String, // 'build' | ||
thin: String // 'configure' | ||
thin: String, // 'configure' | ||
'force-process-config': Boolean // 'configure' | ||
} | ||
@@ -81,0 +82,0 @@ |
@@ -43,3 +43,3 @@ # Installation notes for macOS Catalina (v10.15) | ||
If test succeeded, _you are done_! You should be ready to install `node-gyp`. | ||
If test succeeded, _you are done_! You should be ready to [install](https://github.com/nodejs/node-gyp#installation) `node-gyp`. | ||
@@ -93,3 +93,3 @@ If test failed, there is a problem with your Xcode Command Line Tools installation. [Continue to Solutions](#Solutions). | ||
2. `sudo rm -rf /Library/Developer/CommandLineTools` # Enter root password. | ||
3. `xcode-select --reset` | ||
3. `sudo xcode-select --reset` | ||
4. `xcode-select --install` | ||
@@ -96,0 +96,0 @@ 5. If the [_acid test_ steps above](#The-acid-test) still does _not_ pass then... |
@@ -14,3 +14,3 @@ { | ||
], | ||
"version": "7.1.2", | ||
"version": "8.4.1", | ||
"installVersion": 9, | ||
@@ -28,9 +28,9 @@ "author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)", | ||
"glob": "^7.1.4", | ||
"graceful-fs": "^4.2.3", | ||
"graceful-fs": "^4.2.6", | ||
"make-fetch-happen": "^9.1.0", | ||
"nopt": "^5.0.0", | ||
"npmlog": "^4.1.2", | ||
"request": "^2.88.2", | ||
"npmlog": "^6.0.0", | ||
"rimraf": "^3.0.2", | ||
"semver": "^7.3.2", | ||
"tar": "^6.0.2", | ||
"semver": "^7.3.5", | ||
"tar": "^6.1.2", | ||
"which": "^2.0.2" | ||
@@ -37,0 +37,0 @@ }, |
# `node-gyp` - Node.js native addon build tool | ||
[](https://github.com/nodejs/node-gyp/actions?query=workflow%3ATests+branch%3Amaster) | ||
 | ||
@@ -26,3 +27,3 @@ `node-gyp` is a cross-platform command-line tool written in Node.js for | ||
``` bash | ||
$ npm install -g node-gyp | ||
npm install -g node-gyp | ||
``` | ||
@@ -34,3 +35,3 @@ | ||
* Python v2.7, v3.5, v3.6, v3.7, or v3.8 | ||
* Python v3.6, v3.7, v3.8, or v3.9 | ||
* `make` | ||
@@ -43,3 +44,3 @@ * A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) | ||
* Python v2.7, v3.5, v3.6, v3.7, or v3.8 | ||
* Python v3.6, v3.7, v3.8, or v3.9 | ||
* [Xcode](https://developer.apple.com/xcode/download/) | ||
@@ -52,11 +53,5 @@ * You also need to install the `XCode Command Line Tools` by running `xcode-select --install`. Alternatively, if you already have the full Xcode installed, you can find them under the menu `Xcode -> Open Developer Tool -> More Developer Tools...`. This step will install `clang`, `clang++`, and `make`. | ||
#### Option 1 | ||
Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) using `npm install --global windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator). | ||
#### Option 2 | ||
Install tools and configuration manually: | ||
* Install Visual C++ Build Environment: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) | ||
(using "Visual C++ build tools" workload) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) | ||
(using "Visual C++ build tools" workload) or [Visual Studio Community](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) | ||
(using the "Desktop development with C++" workload) | ||
@@ -71,4 +66,4 @@ * Launch cmd, `npm config set msvs_version 2017` | ||
`node-gyp` requires that you have installed a compatible version of Python, one of: v2.7, v3.5, v3.6, | ||
v3.7, or v3.8. If you have multiple Python versions installed, you can identify which Python | ||
`node-gyp` requires that you have installed a compatible version of Python, one of: v3.6, v3.7, | ||
v3.8, or v3.9. If you have multiple Python versions installed, you can identify which Python | ||
version `node-gyp` should use in one of the following ways: | ||
@@ -79,3 +74,3 @@ | ||
``` bash | ||
$ node-gyp <command> --python /path/to/executable/python | ||
node-gyp <command> --python /path/to/executable/python | ||
``` | ||
@@ -88,3 +83,3 @@ | ||
``` bash | ||
$ npm config set python /path/to/executable/python | ||
npm config set python /path/to/executable/python | ||
``` | ||
@@ -100,2 +95,18 @@ | ||
### Build for Third Party Node.js Runtimes | ||
When building modules for thid party Node.js runtimes like Electron, which have | ||
different build configurations from the official Node.js distribution, you | ||
should use `--dist-url` or `--nodedir` flags to specify the headers of the | ||
runtime to build for. | ||
Also when `--dist-url` or `--nodedir` flags are passed, node-gyp will use the | ||
`config.gypi` shipped in the headers distribution to generate build | ||
configurations, which is different from the default mode that would use the | ||
`process.config` object of the running Node.js instance. | ||
Some old versions of Electron shipped malformed `config.gypi` in their headers | ||
distributions, and you might need to pass `--force-process-config` to node-gyp | ||
to work around configuration errors. | ||
## How to Use | ||
@@ -106,3 +117,3 @@ | ||
``` bash | ||
$ cd my_node_addon | ||
cd my_node_addon | ||
``` | ||
@@ -114,3 +125,3 @@ | ||
``` bash | ||
$ node-gyp configure | ||
node-gyp configure | ||
``` | ||
@@ -121,3 +132,3 @@ | ||
``` bash | ||
$ node-gyp configure --msvs_version=2015 | ||
node-gyp configure --msvs_version=2015 | ||
``` | ||
@@ -132,3 +143,3 @@ | ||
``` bash | ||
$ node-gyp build | ||
node-gyp build | ||
``` | ||
@@ -164,2 +175,4 @@ | ||
The **[docs](./docs/)** directory contains additional documentation on specific node-gyp topics that may be useful if you are experiencing problems installing or building addons using node-gyp. | ||
Some additional resources for Node.js native addons and writing `gyp` configuration files: | ||
@@ -171,3 +184,3 @@ | ||
* [gyp input format reference](https://gyp.gsrc.io/docs/InputFormatReference.md) | ||
* [*"binding.gyp" files out in the wild* wiki page](https://github.com/nodejs/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild) | ||
* [*"binding.gyp" files out in the wild* wiki page](./docs/binding.gyp-files-in-the-wild.md) | ||
@@ -218,2 +231,3 @@ ## Commands | ||
| `--solution=$solution` | Set Visual Studio Solution version (Windows only) | ||
| `--force-process-config` | Force using runtime's `process.config` object to generate `config.gypi` file | ||
@@ -232,3 +246,3 @@ ## Configuration | ||
```bash | ||
$ export npm_config_devdir=/tmp/.gyp | ||
export npm_config_devdir=/tmp/.gyp | ||
``` | ||
@@ -239,3 +253,3 @@ | ||
```console | ||
> set npm_config_devdir=c:\temp\.gyp | ||
set npm_config_devdir=c:\temp\.gyp | ||
``` | ||
@@ -250,3 +264,3 @@ | ||
```bash | ||
$ npm config set [--global] devdir /tmp/.gyp | ||
npm config set [--global] devdir /tmp/.gyp | ||
``` | ||
@@ -253,0 +267,0 @@ |
@@ -14,3 +14,6 @@ 'use strict' | ||
stat: function (file, cb) { cb(null, {}) }, | ||
mkdir: function (dir, options, cb) { cb() } | ||
mkdir: function (dir, options, cb) { cb() }, | ||
promises: { | ||
writeFile: function (file, data) { return Promise.resolve(null) } | ||
} | ||
} | ||
@@ -17,0 +20,0 @@ }) |
'use strict' | ||
const test = require('tap').test | ||
const { test } = require('tap') | ||
const fs = require('fs') | ||
const path = require('path') | ||
const util = require('util') | ||
const http = require('http') | ||
@@ -17,180 +18,131 @@ const https = require('https') | ||
test('download over http', function (t) { | ||
test('download over http', async (t) => { | ||
t.plan(2) | ||
var server = http.createServer(function (req, res) { | ||
t.strictEqual(req.headers['user-agent'], | ||
'node-gyp v42 (node ' + process.version + ')') | ||
const server = http.createServer((req, res) => { | ||
t.strictEqual(req.headers['user-agent'], `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 = { | ||
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') | ||
}) | ||
}) | ||
}) | ||
t.tearDown(() => new Promise((resolve) => server.close(resolve))) | ||
const host = 'localhost' | ||
await new Promise((resolve) => server.listen(0, host, resolve)) | ||
const { port } = server.address() | ||
const gyp = { | ||
opts: {}, | ||
version: '42' | ||
} | ||
const url = `http://${host}:${port}` | ||
const res = await install.test.download(gyp, url) | ||
t.strictEqual(await res.text(), 'ok') | ||
}) | ||
test('download over https with custom ca', function (t) { | ||
test('download over https with custom ca', async (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') | ||
const cafile = path.join(__dirname, '/fixtures/ca.crt') | ||
const [cert, key, ca] = await Promise.all([ | ||
fs.promises.readFile(path.join(__dirname, 'fixtures/server.crt'), 'utf8'), | ||
fs.promises.readFile(path.join(__dirname, 'fixtures/server.key'), 'utf8'), | ||
install.test.readCAFile(cafile) | ||
]) | ||
var cafile = path.join(__dirname, '/fixtures/ca.crt') | ||
var ca = install.test.readCAFile(cafile) | ||
t.strictEqual(ca.length, 1) | ||
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 + ')') | ||
const options = { ca: ca, cert: cert, key: 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() | ||
}) | ||
server.on('clientError', function (err) { | ||
throw err | ||
}) | ||
t.tearDown(() => new Promise((resolve) => server.close(resolve))) | ||
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' | ||
await new Promise((resolve) => server.listen(0, host, resolve)) | ||
const { port } = server.address() | ||
const gyp = { | ||
opts: { cafile }, | ||
version: '42' | ||
} | ||
const url = `https://${host}:${port}` | ||
const res = await install.test.download(gyp, url) | ||
t.strictEqual(await res.text(), 'ok') | ||
}) | ||
test('download over http with proxy', function (t) { | ||
test('download over http with proxy', async (t) => { | ||
t.plan(2) | ||
var server = http.createServer(function (req, res) { | ||
t.strictEqual(req.headers['user-agent'], | ||
'node-gyp v42 (node ' + process.version + ')') | ||
const server = http.createServer((_, res) => { | ||
res.end('ok') | ||
pserver.close(function () { | ||
server.close() | ||
}) | ||
}) | ||
var pserver = http.createServer(function (req, res) { | ||
t.strictEqual(req.headers['user-agent'], | ||
'node-gyp v42 (node ' + process.version + ')') | ||
const pserver = http.createServer((req, res) => { | ||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`) | ||
res.end('proxy ok') | ||
server.close(function () { | ||
pserver.close() | ||
}) | ||
}) | ||
var host = 'localhost' | ||
server.listen(0, host, function () { | ||
var port = this.address().port | ||
pserver.listen(port + 1, host, function () { | ||
var gyp = { | ||
opts: { | ||
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') | ||
}) | ||
}) | ||
}) | ||
}) | ||
t.tearDown(() => Promise.all([ | ||
new Promise((resolve) => server.close(resolve)), | ||
new Promise((resolve) => pserver.close(resolve)) | ||
])) | ||
const host = 'localhost' | ||
await new Promise((resolve) => server.listen(0, host, resolve)) | ||
const { port } = server.address() | ||
await new Promise((resolve) => pserver.listen(port + 1, host, resolve)) | ||
const gyp = { | ||
opts: { | ||
proxy: `http://${host}:${port + 1}`, | ||
noproxy: 'bad' | ||
}, | ||
version: '42' | ||
} | ||
const url = `http://${host}:${port}` | ||
const res = await install.test.download(gyp, url) | ||
t.strictEqual(await res.text(), 'proxy ok') | ||
}) | ||
test('download over http with noproxy', function (t) { | ||
test('download over http with noproxy', async (t) => { | ||
t.plan(2) | ||
var server = http.createServer(function (req, res) { | ||
t.strictEqual(req.headers['user-agent'], | ||
'node-gyp v42 (node ' + process.version + ')') | ||
const server = http.createServer((req, res) => { | ||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`) | ||
res.end('ok') | ||
pserver.close(function () { | ||
server.close() | ||
}) | ||
}) | ||
var pserver = http.createServer(function (req, res) { | ||
t.strictEqual(req.headers['user-agent'], | ||
'node-gyp v42 (node ' + process.version + ')') | ||
const pserver = http.createServer((_, res) => { | ||
res.end('proxy ok') | ||
server.close(function () { | ||
pserver.close() | ||
}) | ||
}) | ||
var host = 'localhost' | ||
server.listen(0, host, function () { | ||
var port = this.address().port | ||
pserver.listen(port + 1, host, function () { | ||
var gyp = { | ||
opts: { | ||
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') | ||
}) | ||
}) | ||
}) | ||
}) | ||
t.tearDown(() => Promise.all([ | ||
new Promise((resolve) => server.close(resolve)), | ||
new Promise((resolve) => pserver.close(resolve)) | ||
])) | ||
const host = 'localhost' | ||
await new Promise((resolve) => server.listen(0, host, resolve)) | ||
const { port } = server.address() | ||
await new Promise((resolve) => pserver.listen(port + 1, host, resolve)) | ||
const gyp = { | ||
opts: { | ||
proxy: `http://${host}:${port + 1}`, | ||
noproxy: host | ||
}, | ||
version: '42' | ||
} | ||
const url = `http://${host}:${port}` | ||
const res = await install.test.download(gyp, url) | ||
t.strictEqual(await res.text(), 'ok') | ||
}) | ||
test('download with missing cafile', function (t) { | ||
test('download with missing cafile', async (t) => { | ||
t.plan(1) | ||
var gyp = { | ||
const gyp = { | ||
opts: { cafile: 'no.such.file' } | ||
} | ||
try { | ||
install.test.download(gyp, {}, 'http://bad/') | ||
await install.test.download(gyp, {}, 'http://bad/') | ||
} catch (e) { | ||
@@ -201,4 +153,4 @@ t.ok(/no.such.file/.test(e.message)) | ||
test('check certificate splitting', function (t) { | ||
var cas = install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt')) | ||
test('check certificate splitting', async (t) => { | ||
const cas = await install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt')) | ||
t.plan(2) | ||
@@ -211,3 +163,3 @@ t.strictEqual(cas.length, 2) | ||
test('download headers (actual)', function (t) { | ||
test('download headers (actual)', async (t) => { | ||
if (process.env.FAST_TEST || | ||
@@ -220,53 +172,40 @@ process.release.name !== 'node' || | ||
t.plan(17) | ||
t.plan(12) | ||
const expectedDir = path.join(devDir, process.version.replace(/^v/, '')) | ||
rimraf(expectedDir, (err) => { | ||
t.ifError(err) | ||
await util.promisify(rimraf)(expectedDir) | ||
const prog = gyp() | ||
prog.parseArgv([]) | ||
prog.devDir = devDir | ||
log.level = 'warn' | ||
install(prog, [], (err) => { | ||
t.ifError(err) | ||
const prog = gyp() | ||
prog.parseArgv([]) | ||
prog.devDir = devDir | ||
log.level = 'warn' | ||
await util.promisify(install)(prog, []) | ||
fs.readFile(path.join(expectedDir, 'installVersion'), 'utf8', (err, data) => { | ||
t.ifError(err) | ||
t.strictEqual(data, '9\n', 'correct installVersion') | ||
}) | ||
const data = await fs.promises.readFile(path.join(expectedDir, 'installVersion'), 'utf8') | ||
t.strictEqual(data, '9\n', 'correct installVersion') | ||
fs.readdir(path.join(expectedDir, 'include/node'), (err, list) => { | ||
t.ifError(err) | ||
const list = await fs.promises.readdir(path.join(expectedDir, 'include/node')) | ||
t.ok(list.includes('common.gypi')) | ||
t.ok(list.includes('config.gypi')) | ||
t.ok(list.includes('node.h')) | ||
t.ok(list.includes('node_version.h')) | ||
t.ok(list.includes('openssl')) | ||
t.ok(list.includes('uv')) | ||
t.ok(list.includes('uv.h')) | ||
t.ok(list.includes('v8-platform.h')) | ||
t.ok(list.includes('v8.h')) | ||
t.ok(list.includes('zlib.h')) | ||
t.ok(list.includes('common.gypi')) | ||
t.ok(list.includes('config.gypi')) | ||
t.ok(list.includes('node.h')) | ||
t.ok(list.includes('node_version.h')) | ||
t.ok(list.includes('openssl')) | ||
t.ok(list.includes('uv')) | ||
t.ok(list.includes('uv.h')) | ||
t.ok(list.includes('v8-platform.h')) | ||
t.ok(list.includes('v8.h')) | ||
t.ok(list.includes('zlib.h')) | ||
}) | ||
const lines = (await fs.promises.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8')).split('\n') | ||
fs.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8', (err, contents) => { | ||
t.ifError(err) | ||
// extract the 3 version parts from the defines to build a valid version string and | ||
// and check them against our current env version | ||
const version = ['major', 'minor', 'patch'].reduce((version, type) => { | ||
const re = new RegExp(`^#define\\sNODE_${type.toUpperCase()}_VERSION`) | ||
const line = lines.find((l) => re.test(l)) | ||
const i = line ? parseInt(line.replace(/^[^0-9]+([0-9]+).*$/, '$1'), 10) : 'ERROR' | ||
return `${version}${type !== 'major' ? '.' : 'v'}${i}` | ||
}, '') | ||
const lines = contents.split('\n') | ||
// extract the 3 version parts from the defines to build a valid version string and | ||
// and check them against our current env version | ||
const version = ['major', 'minor', 'patch'].reduce((version, type) => { | ||
const re = new RegExp(`^#define\\sNODE_${type.toUpperCase()}_VERSION`) | ||
const line = lines.find((l) => re.test(l)) | ||
const i = line ? parseInt(line.replace(/^[^0-9]+([0-9]+).*$/, '$1'), 10) : 'ERROR' | ||
return `${version}${type !== 'major' ? '.' : 'v'}${i}` | ||
}, '') | ||
t.strictEqual(version, process.version) | ||
}) | ||
}) | ||
}) | ||
t.strictEqual(version, process.version) | ||
}) |
@@ -19,9 +19,4 @@ 'use strict' | ||
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, '') | ||
} | ||
t.ok(/Python 3/.test(stdout)) | ||
t.strictEqual(stderr, '') | ||
}) | ||
@@ -70,3 +65,3 @@ proc.stdout.setEncoding('utf-8') | ||
t.ok(/sys\.version_info/.test(args[1])) | ||
cb(null, '2.7.15') | ||
cb(null, '3.9.1') | ||
} | ||
@@ -151,3 +146,3 @@ t.strictEqual(program, | ||
test('find python - no python, use python launcher', function (t) { | ||
t.plan(3) | ||
t.plan(4) | ||
@@ -159,2 +154,3 @@ var f = new TestPythonFinder(null, done) | ||
if (program === 'py.exe') { | ||
t.notEqual(args.indexOf('-3'), -1) | ||
t.notEqual(args.indexOf('-c'), -1) | ||
@@ -169,3 +165,3 @@ return cb(null, 'Z:\\snake.exe') | ||
if (program === 'Z:\\snake.exe') { | ||
cb(null, '2.7.14') | ||
cb(null, '3.9.0') | ||
} else { | ||
@@ -189,5 +185,5 @@ t.fail() | ||
var re = /C:[\\/]Python37[\\/]python[.]exe/ | ||
var f = new TestPythonFinder(null, done) | ||
f.win = true | ||
const expectedProgram = f.winDefaultLocations[0] | ||
@@ -200,3 +196,3 @@ f.execFile = function (program, args, opts, cb) { | ||
cb(new Error('not found')) | ||
} else if (re.test(program) && | ||
} else if (program === expectedProgram && | ||
/sys\.version_info/.test(args[args.length - 1])) { | ||
@@ -212,3 +208,3 @@ cb(null, '3.7.3') | ||
t.strictEqual(err, null) | ||
t.ok(re.test(python)) | ||
t.ok(python === expectedProgram) | ||
} | ||
@@ -215,0 +211,0 @@ }) |
'use strict' | ||
const test = require('tap').test | ||
const install = require('../lib/install').test.install | ||
const { test } = require('tap') | ||
const { test: { install } } = require('../lib/install') | ||
const log = require('npmlog') | ||
require('npmlog').level = 'error' // we expect a warning | ||
log.level = 'error' // we expect a warning | ||
test('EACCES retry once', function (t) { | ||
test('EACCES retry once', async (t) => { | ||
t.plan(3) | ||
var fs = {} | ||
fs.stat = function (path, cb) { | ||
var err = new Error() | ||
err.code = 'EACCES' | ||
cb(err) | ||
t.ok(true) | ||
const fs = { | ||
promises: { | ||
stat (_) { | ||
const err = new Error() | ||
err.code = 'EACCES' | ||
t.ok(true) | ||
throw err | ||
} | ||
} | ||
} | ||
var gyp = {} | ||
gyp.devDir = __dirname | ||
gyp.opts = {} | ||
gyp.opts.ensure = true | ||
gyp.commands = {} | ||
gyp.commands.install = function (argv, cb) { | ||
install(fs, gyp, argv, cb) | ||
const Gyp = { | ||
devDir: __dirname, | ||
opts: { | ||
ensure: true | ||
}, | ||
commands: { | ||
install (argv, cb) { | ||
install(fs, Gyp, argv).then(cb, cb) | ||
}, | ||
remove (_, cb) { | ||
cb() | ||
} | ||
} | ||
} | ||
gyp.commands.remove = function (argv, cb) { | ||
cb() | ||
} | ||
gyp.commands.install([], function (err) { | ||
try { | ||
await install(fs, Gyp, []) | ||
} catch (err) { | ||
t.ok(true) | ||
if (/"pre" versions of node cannot be installed/.test(err.message)) { | ||
t.ok(true) | ||
t.ok(true) | ||
} | ||
}) | ||
} | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 6 instances in 1 package
1977960
2.39%137
7.87%257
5.76%33656
-0.41%63
6.78%12
9.09%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated