Skip to content

Commit

Permalink
src: add support for locally installed headers (#2964)
Browse files Browse the repository at this point in the history
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 <midawson@redhat.com>
Co-authored-by: Luke Karrys <luke@lukekarrys.com>
  • Loading branch information
mhdawson and lukekarrys committed Jan 29, 2024
1 parent 7705a22 commit 3298731
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/configure.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { promises: fs } = require('graceful-fs')
const { promises: fs, readFileSync } = require('graceful-fs')
const path = require('path')
const log = require('./log')
const os = require('os')
Expand All @@ -13,6 +13,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']
Expand All @@ -27,6 +31,28 @@ async function configure (gyp, argv) {
// 'python' should be set by now
process.env.PYTHON = python

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 = 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())
Expand Down
123 changes: 123 additions & 0 deletions test/test-configure-nodedir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use strict'

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')

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() } })

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()
prog.parseArgv(['dummy_prog', 'dummy_script', '--nodedir=' + path.sep + 'usr'])

prog.spawn = function (program, args) {
for (let i = 0; i < args.length; i++) {
if (checkTargetPath('usr', args[i])) {
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 (checkTargetPath(nodedir, args[i])) {
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 (checkTargetPath(nodedir, args[i])) {
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 (checkTargetPath(nodedir, args[i])) {
assert.fail()
}
};
return SPAWN_RESULT(done)
}
configure(prog, [], assert.fail)
})
}
})
1 change: 1 addition & 0 deletions test/test-configure-python.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const configure = requireInject('../lib/configure', {
'graceful-fs': {
openSync: () => 0,
closeSync: () => {},
existsSync: () => {},
promises: {
stat: async () => ({}),
mkdir: async () => {},
Expand Down

0 comments on commit 3298731

Please sign in to comment.