From d320acf9f348f99241359dfe0181405ca54de995 Mon Sep 17 00:00:00 2001 From: Pavel Medvedev Date: Thu, 7 Jan 2016 16:14:06 +0300 Subject: [PATCH 1/4] Add `target_modules_version` variable into generated config.gypi file It may be handy to have multiple native addon binaries for several Node.js versions (i.e. 0.12, 4.X, 5.X). Node.js has a value for the native modules version as `process.versions.modules`. A particular C++ addon can be loaded in JavaScript depending on Node.js version like this: // loads 'addon_win32_x64_m47.node' for Node.js 5.0 on Windows x64 // loads 'addon_linux_x64_m46.node' for Node.js 4.X on Linux x64 var native = require(addon' + process.platform + '_' + process.arch + '_m' + process.versions.modules + '.node'); The target binary file name should be set with `product_name` directive in binding.gyp file in a such way: { 'targets': [ { 'product_name': 'addon_<(target_platform)_<(target_arch)_m<(target_modules_version)' ... # rest of GYP directives } ] } where `target_platform` is a configuration variable with value from Node.js `process.platform` property. But the `process.versions.modules` value can't be used for `target_modules_version` since node-gyp can build targets for different Node.js versions specified with a `--target` command-line option. To resolve this issue, a function getModulesVersion() was added on `configure` step to get a value of `NODE_MODULE_VERSION` defined in `node_version.h` file in a supplied directory with Node.js development files. --- lib/configure.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/configure.js b/lib/configure.js index 2ff476deb1..02cc28a89f 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -81,6 +81,29 @@ function configure (gyp, argv, callback) { } } + function getModulesVersion(nodeDir) { + + var sources = [ + path.join(nodeDir, 'include/node/node_version.h'), + path.join(nodeDir, 'src/node_version.h'), + ] + + for (var i = 0; i < sources.length; ++i) { + try { + var source = fs.readFileSync(sources[i], { encoding: 'utf8'}) + var version = source.match(/^#define\s+NODE_MODULE_VERSION\s+(\d+)/m) + if (!version) { + err = new Error("can't find #define NODE_MODULE_VERSION in " + sources[i]); + callback(err); + } + return parseInt(version[1], 10); + } + catch (e) {} + } + err = new Error("can't read node_version.h in " + nodeDir) + callback(err); + } + function createBuildDir () { log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir) mkdirp(buildDir, function (err, isNew) { @@ -128,6 +151,12 @@ function configure (gyp, argv, callback) { // set the target_arch variable variables.target_arch = gyp.opts.arch || process.arch || 'ia32' + // set the target_platform variable + variables.target_platform = process.platform + + // set the target modules version + variables.target_modules_version = getModulesVersion(nodeDir) + // set the node development directory variables.nodedir = nodeDir From 225cbcd1440bbbbe0d26acd329e4267fa7a6482e Mon Sep 17 00:00:00 2001 From: Pavel Medvedev Date: Fri, 8 Jan 2016 14:56:08 +0300 Subject: [PATCH 2/4] Simplify logic in getModulesVersion() function Use value of release.semver to determinate file name and location with NODE_MODULE_VERSION define, and to read this file depending on target Node.js version. Tested with Node.js versions: 0.8.4, 0.10.25, 0.11.4, 0.11.5, 0.12.0, iojs-1.0.0, iojs-2.0.0, iojs-3.0.0, 4.X, 5.X Added verbose log messages for the target_arch, target_platform, and target_modules_version variables. --- lib/configure.js | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/lib/configure.js b/lib/configure.js index 02cc28a89f..2e062f19cb 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -83,25 +83,35 @@ function configure (gyp, argv, callback) { function getModulesVersion(nodeDir) { - var sources = [ - path.join(nodeDir, 'include/node/node_version.h'), - path.join(nodeDir, 'src/node_version.h'), - ] + var name, source, version + + // NODE_MODULE_VERSION is defined in different files + // depending on the target Node.js version + if (semver.gte(release.semver, '3.0.0')) { + name = 'include/node/node_version.h' + } else if (semver.gt(release.semver, '0.11.4')) { + name = 'src/node_version.h' + } else { + name = 'src/node.h' + } + name = path.join(nodeDir, name) - for (var i = 0; i < sources.length; ++i) { - try { - var source = fs.readFileSync(sources[i], { encoding: 'utf8'}) - var version = source.match(/^#define\s+NODE_MODULE_VERSION\s+(\d+)/m) - if (!version) { - err = new Error("can't find #define NODE_MODULE_VERSION in " + sources[i]); - callback(err); - } - return parseInt(version[1], 10); - } - catch (e) {} + try { + source = fs.readFileSync(name, { encoding: 'utf8'}) + } catch (e) { + err = e + callback(err) + return undefined + } + + version = source.match(/#define\s+NODE_MODULE_VERSION\s+\(?(\S+)\)?/) + if (version === null) { + err = new Error('#define NODE_MODULE_VERSION not found in ' + name) + callback(err) + return undefined } - err = new Error("can't read node_version.h in " + nodeDir) - callback(err); + version = parseInt(version[1]) + return version } function createBuildDir () { @@ -150,12 +160,15 @@ function configure (gyp, argv, callback) { // set the target_arch variable variables.target_arch = gyp.opts.arch || process.arch || 'ia32' + log.verbose('build/' + configFilename, 'target arch: ' + variables.target_arch) // set the target_platform variable variables.target_platform = process.platform + log.verbose('build/' + configFilename, 'target platform: ' + variables.target_platform) // set the target modules version variables.target_modules_version = getModulesVersion(nodeDir) + log.verbose('build/' + configFilename, 'modules version: ' + variables.target_modules_version) // set the node development directory variables.nodedir = nodeDir From 1844911c3e6e3fa3e19212a58a06545e8dbb3a7e Mon Sep 17 00:00:00 2001 From: Pavel Medvedev Date: Mon, 20 Jun 2016 14:04:46 +0300 Subject: [PATCH 3/4] Add tests for nodeModulesVersion() Rename getModulesVersion to nodeModulesVersion function and move it out of configure() function for testing purpose. Expose nodeModulesVersion() function in `configure.test` to call it tests. Run tests for the current Node release and for all installed targets. --- lib/configure.js | 74 ++++++++++++++++--------------- test/test-node-modules-version.js | 34 ++++++++++++++ 2 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 test/test-node-modules-version.js diff --git a/lib/configure.js b/lib/configure.js index 2e062f19cb..9cf69db775 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -1,6 +1,7 @@ module.exports = exports = configure module.exports.test = { findAccessibleSync: findAccessibleSync, - findPython: findPython } + findPython: findPython, + nodeModulesVersion: nodeModulesVersion } /** * Module dependencies. @@ -81,39 +82,6 @@ function configure (gyp, argv, callback) { } } - function getModulesVersion(nodeDir) { - - var name, source, version - - // NODE_MODULE_VERSION is defined in different files - // depending on the target Node.js version - if (semver.gte(release.semver, '3.0.0')) { - name = 'include/node/node_version.h' - } else if (semver.gt(release.semver, '0.11.4')) { - name = 'src/node_version.h' - } else { - name = 'src/node.h' - } - name = path.join(nodeDir, name) - - try { - source = fs.readFileSync(name, { encoding: 'utf8'}) - } catch (e) { - err = e - callback(err) - return undefined - } - - version = source.match(/#define\s+NODE_MODULE_VERSION\s+\(?(\S+)\)?/) - if (version === null) { - err = new Error('#define NODE_MODULE_VERSION not found in ' + name) - callback(err) - return undefined - } - version = parseInt(version[1]) - return version - } - function createBuildDir () { log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir) mkdirp(buildDir, function (err, isNew) { @@ -167,7 +135,7 @@ function configure (gyp, argv, callback) { log.verbose('build/' + configFilename, 'target platform: ' + variables.target_platform) // set the target modules version - variables.target_modules_version = getModulesVersion(nodeDir) + variables.target_modules_version = nodeModulesVersion(nodeDir) log.verbose('build/' + configFilename, 'modules version: ' + variables.target_modules_version) // set the node development directory @@ -353,6 +321,42 @@ function configure (gyp, argv, callback) { } +/** + * Returns value of NODE_MODULE_VERSION define + * from Node source code in specified directory + * for specified Node version + */ +function nodeModulesVersion(nodeDir) { + var versionStr = path.basename(nodeDir) + + // parse version from the base name of nodeDir + // probably removing optional `releaseName-` prefix + var nodeVersion = semver.valid(versionStr) + || semver.valid(versionStr.substr(versionStr.indexOf('-') + 1)) + + if (!nodeVersion) { + throw new Error('Can\'t extract target version from ' + nodeDir) + } + + // NODE_MODULE_VERSION may be defined in different files + // depending on the target Node.js version + if (semver.gte(nodeVersion, '3.0.0')) { + nodeVersion = 'include/node/node_version.h' + } else if (semver.gt(nodeVersion, '0.11.4')) { + nodeVersion = 'src/node_version.h' + } else { + nodeVersion = 'src/node.h' + } + nodeVersion = path.join(nodeDir, nodeVersion) + + var modulesVersion = fs.readFileSync(nodeVersion, { encoding: 'utf8'}) + .match(/#define\s+NODE_MODULE_VERSION\s+\(?(\S+)\)?/) + if (!modulesVersion) { + throw new Error('#define NODE_MODULE_VERSION not found in ' + nodeVersion) + } + return parseInt(modulesVersion[1]).toString() +} + /** * Returns the first file or directory from an array of candidates that is * readable by the current user, or undefined if none of the candidates are diff --git a/test/test-node-modules-version.js b/test/test-node-modules-version.js new file mode 100644 index 0000000000..08a4c46dfa --- /dev/null +++ b/test/test-node-modules-version.js @@ -0,0 +1,34 @@ +'use strict' + +var test = require('tape') +var path = require('path') +var semver = require('semver') +var processRelease = require('../lib/process-release') +var configure = require('../lib/configure') + +var gyp = require('../lib/node-gyp')() // for gyp.devDir +gyp.parseArgv([]) // to initialize gyp.opts + +test('modules version for the current release', function (t) { + t.plan(1) + + var release = processRelease([], gyp, process.version) + var nodeDir = path.join(gyp.devDir, release.versionDir) + var modulesVersion = configure.test.nodeModulesVersion(nodeDir) + t.equal(modulesVersion, process.versions.modules, + nodeDir + ' modulesVersion=' + modulesVersion) +}) + +test('node modules version for installed targets', function (t) { + gyp.commands.list([], function(err, versions) { + if (err) return t.fail(err) + t.plan(versions.length) + + versions.forEach(function(version) { + var nodeDir = path.join(gyp.devDir, version) + var modulesVersion = configure.test.nodeModulesVersion(nodeDir) + t.ok(modulesVersion > 0, + nodeDir + ' modulesVersion=' + modulesVersion) + }) + }) +}) From a6b6f9e54fbf3a82d07d72cd891917eb5e1a17a9 Mon Sep 17 00:00:00 2001 From: Pavel Medvedev Date: Sat, 16 Jul 2016 12:26:23 +0300 Subject: [PATCH 4/4] Fix code style --- lib/configure.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/configure.js b/lib/configure.js index 9cf69db775..0a08abe0e2 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -128,15 +128,18 @@ function configure (gyp, argv, callback) { // set the target_arch variable variables.target_arch = gyp.opts.arch || process.arch || 'ia32' - log.verbose('build/' + configFilename, 'target arch: ' + variables.target_arch) + log.verbose('build/' + configFilename, + 'target arch: ' + variables.target_arch) // set the target_platform variable variables.target_platform = process.platform - log.verbose('build/' + configFilename, 'target platform: ' + variables.target_platform) + log.verbose('build/' + configFilename, + 'target platform: ' + variables.target_platform) // set the target modules version variables.target_modules_version = nodeModulesVersion(nodeDir) - log.verbose('build/' + configFilename, 'modules version: ' + variables.target_modules_version) + log.verbose('build/' + configFilename, + 'modules version: ' + variables.target_modules_version) // set the node development directory variables.nodedir = nodeDir @@ -332,7 +335,7 @@ function nodeModulesVersion(nodeDir) { // parse version from the base name of nodeDir // probably removing optional `releaseName-` prefix var nodeVersion = semver.valid(versionStr) - || semver.valid(versionStr.substr(versionStr.indexOf('-') + 1)) + || semver.valid(versionStr.substr(versionStr.indexOf('-') + 1)) if (!nodeVersion) { throw new Error('Can\'t extract target version from ' + nodeDir) @@ -349,7 +352,7 @@ function nodeModulesVersion(nodeDir) { } nodeVersion = path.join(nodeDir, nodeVersion) - var modulesVersion = fs.readFileSync(nodeVersion, { encoding: 'utf8'}) + var modulesVersion = fs.readFileSync(nodeVersion, { encoding: 'utf8' }) .match(/#define\s+NODE_MODULE_VERSION\s+\(?(\S+)\)?/) if (!modulesVersion) { throw new Error('#define NODE_MODULE_VERSION not found in ' + nodeVersion)