From a5ac175b94066d5f8804bae8ae71a5cfa08c1f3b Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 12 Dec 2023 21:19:50 -0500 Subject: [PATCH 1/7] feat: add support for locally installed headers Some linux distros allow headers to be installed through tools like rpm. If the runtime sets process.config.variables.use_prefix_to_find_headers, look for matching headers based on the directory set for the prefix in process.config.variables.prefix Signed-off-by: Michael Dawson --- lib/configure.js | 27 ++++++++ test/test-configure-nodedir.js | 115 +++++++++++++++++++++++++++++++++ test/test-configure-python.js | 1 + 3 files changed, 143 insertions(+) create mode 100644 test/test-configure-nodedir.js diff --git a/lib/configure.js b/lib/configure.js index 8da41a849d..92ecd83dba 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -1,6 +1,7 @@ 'use strict' const { promises: fs } = require('graceful-fs') +const fsSync = require('graceful-fs') const path = require('path') const log = require('./log') const os = require('os') @@ -13,6 +14,10 @@ const { findAccessibleSync } = require('./util') const { findPython } = require('./find-python') const { findVisualStudio } = win ? require('./find-visualstudio') : {} +const majorRe = /^#define NODE_MAJOR_VERSION (\d+)/m +const minorRe = /^#define NODE_MINOR_VERSION (\d+)/m +const patchRe = /^#define NODE_PATCH_VERSION (\d+)/m + async function configure (gyp, argv) { const buildDir = path.resolve('build') const configNames = ['config.gypi', 'common.gypi'] @@ -27,6 +32,28 @@ async function configure (gyp, argv) { // 'python' should be set by now process.env.PYTHON = python + const prefix = process.config.variables.node_prefix + if (!gyp.opts.nodedir && + process.config.variables.use_prefix_to_find_headers) { + // check if the headers can be found using the prefix specified + // at build time. Use them if they match the version expected + let availVersion + try { + const nodeVersionH = fsSync.readFileSync(path.join(prefix, + 'include', 'node', 'node_version.h'), { encoding: 'utf8' }) + const major = nodeVersionH.match(majorRe)[1] + const minor = nodeVersionH.match(minorRe)[1] + const patch = nodeVersionH.match(patchRe)[1] + availVersion = major + '.' + minor + '.' + patch + } catch {} + if (availVersion === release.version) { + // ok version matches, use the headers + gyp.opts.nodedir = prefix + log.verbose('using local node headers based on prefix', + 'setting nodedir to ' + gyp.opts.nodedir) + } + } + if (gyp.opts.nodedir) { // --nodedir was specified. use that for the dev files nodeDir = gyp.opts.nodedir.replace(/^~/, os.homedir()) diff --git a/test/test-configure-nodedir.js b/test/test-configure-nodedir.js new file mode 100644 index 0000000000..71217e992e --- /dev/null +++ b/test/test-configure-nodedir.js @@ -0,0 +1,115 @@ +'use strict' + +const { describe, it } = require('mocha') +const assert = require('assert') +const path = require('path') +const gyp = require('../lib/node-gyp') +const requireInject = require('require-inject') +const semver = require('semver') + +const versionSemver = semver.parse(process.version) + +const configure = requireInject('../lib/configure', { + 'graceful-fs': { + openSync: () => 0, + closeSync: () => {}, + existsSync: () => true, + readFileSync: () => '#define NODE_MAJOR_VERSION ' + versionSemver.major + '\n' + + '#define NODE_MINOR_VERSION ' + versionSemver.minor + '\n' + + '#define NODE_PATCH_VERSION ' + versionSemver.patch + '\n', + promises: { + stat: async () => ({}), + mkdir: async () => {}, + writeFile: async () => {} + } + } +}) + +const configure2 = requireInject('../lib/configure', { + 'graceful-fs': { + openSync: () => 0, + closeSync: () => {}, + existsSync: () => true, + readFileSync: () => '#define NODE_MAJOR_VERSION 8\n' + + '#define NODE_MINOR_VERSION 0\n' + + '#define NODE_PATCH_VERSION 0\n', + promises: { + stat: async () => ({}), + mkdir: async () => {}, + writeFile: async () => {} + } + } +}) + +const SPAWN_RESULT = cb => ({ on: function () { cb() } }) + +describe('configure-nodedir', function () { + it('configure nodedir with node-gyp command line', function (done) { + const prog = gyp() + prog.parseArgv(['dummy_prog', 'dummy_script', '--nodedir=/usr']) + + prog.spawn = function (program, args) { + for (let i = 0; i < args.length; i++) { + if (path.join(path.sep, 'usr', 'include', 'node', + 'common.gypi').localeCompare(args[i]) === 0) { + return SPAWN_RESULT(done) + } + }; + assert.fail() + } + configure(prog, [], assert.fail) + }) + + if (process.config.variables.use_prefix_to_find_headers) { + it('use-prefix-to-find-headers build time option - match', function (done) { + const prog = gyp() + prog.parseArgv(['dummy_prog', 'dummy_script']) + + prog.spawn = function (program, args) { + for (let i = 0; i < args.length; i++) { + const nodedir = process.config.variables.node_prefix + if (path.join(path.sep, nodedir, 'include', 'node', + 'common.gypi').localeCompare(args[i]) === 0) { + return SPAWN_RESULT(done) + } + }; + assert.fail() + } + configure(prog, [], assert.fail) + }) + + it('use-prefix-to-find-headers build time option - no match', function (done) { + const prog = gyp() + prog.parseArgv(['dummy_prog', 'dummy_script']) + + prog.spawn = function (program, args) { + for (let i = 0; i < args.length; i++) { + const nodedir = process.config.variables.node_prefix + if (path.join(path.sep, nodedir, 'include', 'node', + 'common.gypi').localeCompare(args[i]) === 0) { + assert.fail() + } + }; + return SPAWN_RESULT(done) + } + configure2(prog, [], assert.fail) + }) + + it('use-prefix-to-find-headers build time option, target specified', function (done) { + const prog = gyp() + prog.parseArgv(['dummy_prog', 'dummy_script', '--target=8.0.0']) + + prog.spawn = function (program, args) { + for (let i = 0; i < args.length; i++) { + const nodedir = process.config.variables.node_prefix + if (path.join(path.sep, nodedir, 'include', 'node', + 'common.gypi').localeCompare(args[i]) === 0) { + assert.fail() + } + }; + return SPAWN_RESULT(done) + } + configure(prog, [], assert.fail) + }) + } +}) diff --git a/test/test-configure-python.js b/test/test-configure-python.js index eee230a496..094e79182c 100644 --- a/test/test-configure-python.js +++ b/test/test-configure-python.js @@ -11,6 +11,7 @@ const configure = requireInject('../lib/configure', { 'graceful-fs': { openSync: () => 0, closeSync: () => {}, + existsSync: () => {}, promises: { stat: async () => ({}), mkdir: async () => {}, From d17602b88e245a5be366b24599ee47aa607947a1 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 19 Jan 2024 09:44:43 -0500 Subject: [PATCH 2/7] squash: address windows failure Signed-off-by: Michael Dawson --- test/test-configure-nodedir.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-configure-nodedir.js b/test/test-configure-nodedir.js index 71217e992e..0877f43d31 100644 --- a/test/test-configure-nodedir.js +++ b/test/test-configure-nodedir.js @@ -46,7 +46,7 @@ const SPAWN_RESULT = cb => ({ on: function () { cb() } }) describe('configure-nodedir', function () { it('configure nodedir with node-gyp command line', function (done) { const prog = gyp() - prog.parseArgv(['dummy_prog', 'dummy_script', '--nodedir=/usr']) + prog.parseArgv(['dummy_prog', 'dummy_script', '--nodedir=' + path.sep + 'usr']) prog.spawn = function (program, args) { for (let i = 0; i < args.length; i++) { From 599932b12b2493b34039df2ebc309ebeb7f69289 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 19 Jan 2024 12:25:13 -0500 Subject: [PATCH 3/7] squash: address windows ci failures Signed-off-by: Michael Dawson --- test/test-configure-nodedir.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/test-configure-nodedir.js b/test/test-configure-nodedir.js index 0877f43d31..a6debded06 100644 --- a/test/test-configure-nodedir.js +++ b/test/test-configure-nodedir.js @@ -3,6 +3,7 @@ const { describe, it } = require('mocha') const assert = require('assert') const path = require('path') +const os = require('os') const gyp = require('../lib/node-gyp') const requireInject = require('require-inject') const semver = require('semver') @@ -43,6 +44,17 @@ const configure2 = requireInject('../lib/configure', { const SPAWN_RESULT = cb => ({ on: function () { cb() } }) +const driveLetter = os.platform() === 'win32' ? `${process.cwd().split(path.sep)[0]}` : '' +function checkTargetPath (target, value) { + let targetPath = path.join(path.sep, target, 'include', + 'node', 'common.gypi') + if (process.platform === 'win32') { + targetPath = driveLetter + targetPath + } + + return targetPath.localeCompare(value) === 0 +} + describe('configure-nodedir', function () { it('configure nodedir with node-gyp command line', function (done) { const prog = gyp() @@ -50,8 +62,7 @@ describe('configure-nodedir', function () { prog.spawn = function (program, args) { for (let i = 0; i < args.length; i++) { - if (path.join(path.sep, 'usr', 'include', 'node', - 'common.gypi').localeCompare(args[i]) === 0) { + if (checkTargetPath('usr', args[i])) { return SPAWN_RESULT(done) } }; @@ -68,8 +79,7 @@ describe('configure-nodedir', function () { prog.spawn = function (program, args) { for (let i = 0; i < args.length; i++) { const nodedir = process.config.variables.node_prefix - if (path.join(path.sep, nodedir, 'include', 'node', - 'common.gypi').localeCompare(args[i]) === 0) { + if (checkTargetPath(nodedir, args[i])) { return SPAWN_RESULT(done) } }; @@ -85,8 +95,7 @@ describe('configure-nodedir', function () { prog.spawn = function (program, args) { for (let i = 0; i < args.length; i++) { const nodedir = process.config.variables.node_prefix - if (path.join(path.sep, nodedir, 'include', 'node', - 'common.gypi').localeCompare(args[i]) === 0) { + if (checkTargetPath(nodedir, args[i])) { assert.fail() } }; @@ -102,8 +111,7 @@ describe('configure-nodedir', function () { prog.spawn = function (program, args) { for (let i = 0; i < args.length; i++) { const nodedir = process.config.variables.node_prefix - if (path.join(path.sep, nodedir, 'include', 'node', - 'common.gypi').localeCompare(args[i]) === 0) { + if (checkTargetPath(nodedir, args[i])) { assert.fail() } }; From 178fcd512da5a2e68260da7e5c0e19f567b2269f Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 19 Jan 2024 13:30:25 -0500 Subject: [PATCH 4/7] address review comments --- lib/configure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configure.js b/lib/configure.js index 92ecd83dba..97de9b69b6 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -32,11 +32,11 @@ async function configure (gyp, argv) { // 'python' should be set by now process.env.PYTHON = python - const prefix = process.config.variables.node_prefix if (!gyp.opts.nodedir && process.config.variables.use_prefix_to_find_headers) { // check if the headers can be found using the prefix specified // at build time. Use them if they match the version expected + const prefix = process.config.variables.node_prefix let availVersion try { const nodeVersionH = fsSync.readFileSync(path.join(prefix, From 7315afdda4ad800e1689cf0cf5fd6ab585cf57d3 Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Mon, 29 Jan 2024 11:07:43 -0700 Subject: [PATCH 5/7] Update lib/configure.js --- lib/configure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configure.js b/lib/configure.js index 97de9b69b6..f5e3329bec 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -39,7 +39,7 @@ async function configure (gyp, argv) { const prefix = process.config.variables.node_prefix let availVersion try { - const nodeVersionH = fsSync.readFileSync(path.join(prefix, + const nodeVersionH = readFileSync(path.join(prefix, 'include', 'node', 'node_version.h'), { encoding: 'utf8' }) const major = nodeVersionH.match(majorRe)[1] const minor = nodeVersionH.match(minorRe)[1] From 65292ffa2c85404b26e69d6d26cb27264c5de71a Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Mon, 29 Jan 2024 11:07:51 -0700 Subject: [PATCH 6/7] Update lib/configure.js --- lib/configure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configure.js b/lib/configure.js index f5e3329bec..474fb788bf 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -1,6 +1,6 @@ 'use strict' -const { promises: fs } = require('graceful-fs') +const { promises: fs, readFileSync } = require('graceful-fs') const fsSync = require('graceful-fs') const path = require('path') const log = require('./log') From b4dcacdb3ff3da1acdc1375c0299a54dc3c8fc7d Mon Sep 17 00:00:00 2001 From: Luke Karrys Date: Mon, 29 Jan 2024 11:10:14 -0700 Subject: [PATCH 7/7] Update lib/configure.js --- lib/configure.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/configure.js b/lib/configure.js index 474fb788bf..e4b8c94e3d 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -1,7 +1,6 @@ 'use strict' const { promises: fs, readFileSync } = require('graceful-fs') -const fsSync = require('graceful-fs') const path = require('path') const log = require('./log') const os = require('os')