node-cmake
Advanced tools
Comparing version 1.3.0 to 2.0.0
cmake_minimum_required(VERSION 3.1) | ||
project(addon) | ||
project(example VERSION 1.0.0) | ||
list(APPEND CMAKE_MODULE_PATH | ||
${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-cmake | ||
) | ||
include(../NodeJS.cmake) | ||
nodejs_init() | ||
find_package(NodeJS) | ||
add_nodejs_module(${PROJECT_NAME} src/hello.cc) | ||
add_nodejs_module(${PROJECT_NAME} src/hello.cc) |
'use strict'; | ||
module.exports = require('node-cmake')('addon'); | ||
module.exports = require('node-cmake')('example'); |
@@ -14,8 +14,8 @@ { | ||
"scripts": { | ||
"install": "ncmake --build" | ||
"install": "ncmake rebuild" | ||
}, | ||
"repository": {}, | ||
"devDependencies": { | ||
"node-cmake": "^1.0.0" | ||
"node-cmake": "^2.0.0" | ||
} | ||
} |
Native Addon | ||
============ | ||
The native addon, adjusted to use Nan for cross-variant compatibility, | ||
from the node.js documentation site for | ||
[Addons](https://nodejs.org/api/addons.html). | ||
An example native addon, adjusted to use Nan for cross-variant compatibility, | ||
from the Node.js documentation site for [Addons](https://nodejs.org/api/addons.html). | ||
## Development Setup | ||
This module requires node-cmake and is a part of node-cmake's distribution. | ||
Now that node-cmake is published to npm, it is no longer necessary to | ||
use `npm link` as the dependency can be directly downloaded. | ||
This module requires node-cmake and is a part of node-cmake's distribution. A normal | ||
project should copy NodeJS.cmake to their own project; this just refers to its | ||
location in this project, one directory above. | ||
However, this is the best way to test development changes to the project. | ||
From the root of this project run: | ||
npm link | ||
Then, in this folder, run: | ||
npm link node-cmake | ||
## Building | ||
@@ -38,2 +28,2 @@ | ||
If everything worked correctly, you should see it print "world". | ||
If everything worked correctly, you should see it print "world". |
80
index.js
@@ -5,57 +5,41 @@ 'use strict'; | ||
var _ = require('lodash'); | ||
var mothership = require('mothership'); | ||
function trueIdentity () { return true; } | ||
function requireCMakeModule (name, conf) { | ||
// Find the module root by finding the nearest package.json | ||
// Use the path of the module that required this as the starting point | ||
function requireNativeModule(name, debug) { | ||
// Search relative to the file that included this one | ||
var base = path.dirname(module.parent.filename); | ||
var json = mothership.sync(base, trueIdentity); | ||
var root; | ||
if(json) { | ||
root = path.dirname(json.path); | ||
} | ||
else { | ||
throw Error('Unable to find module root'); | ||
} | ||
// Suffixes to search for (in each mode) | ||
// Both are used, debug just changes which is tried first | ||
var search = { | ||
debug: path.join('build', 'Debug', name + '.node'), | ||
release: path.join('build', 'Release', name + '.node') | ||
}; | ||
// Allow search path override | ||
conf = _.merge({}, requireCMakeModule.build, conf || {}); | ||
// Allow single path definitions | ||
if(!(conf.output instanceof Array)) conf.output = [conf.output]; | ||
if(!(conf.config instanceof Array)) conf.config = [conf.config]; | ||
// Search for the module in the specified locations | ||
// Nest the searching so that things are tried in succession | ||
var nativeModule; | ||
if(!conf.output.some(function searchOutput (output) { | ||
return conf.config.some(function searchConfig (config) { | ||
nativeModule = path.join( | ||
root, output, process.platform, process.arch, config, name + '.node' | ||
); | ||
var root = base; | ||
var location; | ||
var same = 0; | ||
var found = false; | ||
// Walk upward to the root of the current drive | ||
while(same < 2 || found) { | ||
try { | ||
location = path.join(root, (debug) ? search.debug : search.release); | ||
found = fs.statSync(location); | ||
} | ||
catch(e) {} | ||
if(!found) { | ||
try { | ||
return fs.statSync(nativeModule); | ||
location = path.join(root, (debug) ? search.release : search.debug); | ||
found = fs.statSync(location); | ||
} | ||
catch(e) { return false; } | ||
}); | ||
})) { | ||
throw Error('Unable to find native module'); | ||
catch(e) {} | ||
} | ||
if(found) break; | ||
root = path.dirname(root); | ||
if(root == path.dirname(root)) same++; | ||
} | ||
// Require the native module and return the loaded content | ||
return require(nativeModule); | ||
if(!found) throw new Error('Unable to find native module'); | ||
return require(location); | ||
} | ||
requireCMakeModule.build = { | ||
output: [ | ||
'out', 'build' | ||
], | ||
config: [ | ||
'MinSizeRel', 'Release', 'RelWithDebInfo', 'Debug' | ||
] | ||
}; | ||
module.exports = requireCMakeModule; | ||
module.exports = requireNativeModule; |
@@ -1,306 +0,255 @@ | ||
#!/usr/bin/env node | ||
'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var spawn = require('child_process').spawn; | ||
var _ = require('lodash'); | ||
var mothership = require('mothership'); | ||
var yargs = require('yargs'); | ||
var which = require('which'); | ||
var rimraf = require('rimraf'); | ||
var mkdirp = require('mkdirp'); | ||
// Make sure that the arguments are valid | ||
function usage (code) { | ||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + | ||
' [options] <build-directory>'); | ||
process.exit(code || 1); | ||
} | ||
if(process.argv.length < 3) usage(); | ||
var buildDir = 'build'; | ||
// Find the cmake executable | ||
var cmake = which.sync('cmake'); | ||
// Main usage strings | ||
var argparse = yargs | ||
.usage('$0 [options] <command>'); | ||
// Define default arguments for the program | ||
var args = { | ||
args: { | ||
generate: [], | ||
build: [] | ||
}, | ||
generator: {}, | ||
flags: {}, | ||
arch: process.arch, | ||
platform: process.platform, | ||
config: 'MinSizeRel', | ||
build: false, | ||
clean: false | ||
}; | ||
// Primary options | ||
// Where appropriate, node-gyp argument names have been retained for | ||
// compatibility. All unsupported options will be ignored by the parser. | ||
argparse = argparse | ||
.global(['debug', 'target', 'dist-url']) | ||
.describe('debug', 'Build with debug symbols') | ||
.describe('target', 'Version of Node.js to use') | ||
.describe('dist-url', 'Set the download server for dependencies'); | ||
// Process command line arguments | ||
var rarg = process.argv.slice(2, process.argv.length); | ||
var next = null; | ||
var variantSet = false; | ||
var versionSet = false; | ||
rarg.map(function parseArguments (arg) { | ||
var argu = arg.toUpperCase(); | ||
if(next) { | ||
_.set(args, next, arg); | ||
next = null; | ||
} | ||
else if(_.startsWith(argu, '-D')) { | ||
arg = arg.substr(2).split('='); | ||
args.flags[arg[0]] = arg[1]; | ||
} | ||
else if(argu === '--BUILD') { | ||
args.build = true; | ||
} | ||
else if(argu === '--CLEAN-FIRST') { | ||
args.clean = true; | ||
} | ||
else if(argu === '--CONFIG') { | ||
next = 'config'; | ||
} | ||
else if(argu === '--TARGET') { | ||
next = 'target'; | ||
} | ||
else if(argu === '-C') { | ||
next = 'standard'; | ||
} | ||
else if(argu === '-Z') { | ||
args.download = true; | ||
} | ||
else if(argu === '-NZ') { | ||
args.download = false; | ||
} | ||
else if(argu === '-G') { | ||
next = 'generator.name'; | ||
} | ||
else if(argu === '-T') { | ||
next = 'generator.toolset'; | ||
} | ||
else if(argu === '-A') { | ||
next = 'arch'; | ||
} | ||
else if(argu === '-P') { | ||
next = 'platform'; | ||
} | ||
else if(argu === '-S') { | ||
next = 'variant'; | ||
variantSet = true; | ||
} | ||
else if(argu === '-V') { | ||
next = 'version'; | ||
versionSet = true; | ||
} | ||
else { | ||
args.args[args.build ? 'build' : 'generate'].push(arg); | ||
} | ||
}); | ||
// New options unique to CMake / ncmake | ||
argparse = argparse | ||
.global(['generator', 'name']) | ||
.describe('name', 'The executable target (node/iojs)') | ||
.describe('generator', 'The CMake generator to use'); | ||
if(variantSet && !versionSet) { | ||
console.log('Variant set without version - aborting build'); | ||
process.exit(1); | ||
} | ||
// Commands (task to execute) | ||
argparse = argparse | ||
.command('help', 'Shows the help dialog') | ||
.command('build', 'Builds the native addon') | ||
.command('clean', 'Cleans the build') | ||
.command('distclean', 'Removes all build files') | ||
.command('configure', 'Runs CMake to generate the project configuration') | ||
.command('rebuild', 'Runs clean, configure and build'); | ||
// If there are no extra arguments, use the default folder | ||
if((args.args.build.length+args.args.generate.length) === 0) { | ||
args.directory = 'out'; | ||
} | ||
// Otherwise the last argument MUST be the build directory | ||
else args.directory = process.argv[process.argv.length-1]; | ||
// Deprecated commands from node-gyp | ||
var compat = 'Deprecated node-gyp command (no-op)'; | ||
argparse = argparse | ||
.command('install', compat) | ||
.command('list', compat) | ||
.command('remove', compat); | ||
// Load the configuration options from the package.json | ||
function trueIdentity () { return true; } | ||
var json = mothership.sync(process.cwd(), trueIdentity); | ||
var base = (json.pack || {}).cmake || {}; | ||
// Mark advanced options | ||
argparse = argparse | ||
.group(['target', 'dist-url', 'name', 'generator'], 'Advanced:'); | ||
// Merge together arguments and configuration | ||
var conf = _.merge({}, base.default || {}, base[process.platform] || {}, args); | ||
// Aliases and settings for the options | ||
argparse = argparse | ||
.boolean('debug') | ||
.alias('debug', 'd') | ||
.alias('help', 'h') | ||
.alias('generator', 'g'); | ||
// Convert version shortcut to installed version | ||
if(conf.version && conf.version === 'installed') { | ||
conf.version = process.version.substr(1); | ||
} | ||
// Set generator if not set | ||
if(!conf.generator.name) { | ||
// Use Xcode on OSX as the generator | ||
if(conf.platform === 'darwin') { | ||
conf.generator.name = 'Xcode'; | ||
// Use Ninja on platforms where it is installed as a default | ||
// (since its significantly faster than make) | ||
var ninja, generator = 'default'; | ||
if(process.platform === 'darwin' || process.platform == 'linux') { | ||
try { | ||
ninja = which.sync('ninja'); | ||
generator = 'Ninja'; | ||
} | ||
// Use Ninja as the generator if its available as an executable | ||
// (except on windows, where MSVC (the default) should be used) | ||
else if(conf.platform !== 'win32') { | ||
try { | ||
which.sync('ninja'); | ||
conf.generator.name = 'Ninja'; | ||
conf.verbose = '-v'; | ||
} | ||
catch(e) {} | ||
} | ||
catch(err) {} | ||
} | ||
// Build type should always match config | ||
// Ignored by generators that use configuration | ||
conf.flags.CMAKE_BUILD_TYPE = conf.config; | ||
// Defaults for options that need them | ||
argparse = argparse | ||
.default('generator', generator) | ||
.default('debug', false); | ||
// Allow download overloading | ||
// Bit of a complicated case, since false is a valid value | ||
if(conf.flags.NodeJS_DOWNLOAD && _.isUndefined(conf.download)) { | ||
conf.download = ( | ||
conf.flags.NodeJS_DOWNLOAD.toUpperCase() === 'ON' || | ||
conf.flags.NodeJS_DOWNLOAD.toUpperCase() === 'TRUE' | ||
); | ||
} | ||
else if(!_.isUndefined(conf.download)) | ||
conf.flags.NodeJS_DOWNLOAD = (conf.download) ? 'On' : 'Off'; | ||
// Support a version string, and a help argument (in addition to | ||
// the help command) | ||
argparse = argparse.version(); | ||
argparse = argparse.help(); | ||
// Configure CXX standard to use | ||
if(conf.flags.NodeJS_CXX_STANDARD && !conf.standard) { | ||
conf.standard = conf.flags.NodeJS_CXX_STANDARD; | ||
} | ||
else if(conf.standard) conf.flags.NodeJS_CXX_STANDARD = conf.standard; | ||
// Warning type - used to differentiate output in promise chain | ||
function Warning(msg) { this.message = msg; } | ||
// Configure version and variant | ||
if(conf.flags.NodeJS_VARIANT && !conf.variant) { | ||
conf.variant = conf.flags.NodeJS_VARIANT; | ||
// Catch-all function for node-gyp deprecated commands | ||
function deprecated() { | ||
return new Promise(function (resolve, reject) { | ||
var warn = new Warning('node-gyp deprecated command invoked. ' + | ||
'This is not supported in node-cmake, consider updating your build'); | ||
warn.code = 0; // Exit cleanly (for tools that still use this command) | ||
reject(warn); | ||
}); | ||
} | ||
else if(conf.variant) conf.flags.NodeJS_VARIANT = conf.variant; | ||
if(conf.flags.NodeJS_VERSION && !conf.version) { | ||
conf.version = conf.flags.NodeJS_VERSION; | ||
} | ||
else if(conf.version) conf.flags.NodeJS_VERSION = conf.version; | ||
// Always specify node architecture properties | ||
// Other means of resolving these are for external CMake invocation | ||
conf.flags.NodeJS_PLATFORM = conf.platform; | ||
conf.flags.NodeJS_ARCH = conf.arch; | ||
// The list of accepted commands (mirror node-gyp's API) | ||
var commands = { | ||
help: function () { | ||
return new Promise(function (resolve) { | ||
argparse.showHelp(); | ||
resolve(); | ||
}); | ||
}, | ||
distclean: function (argv, cmake) { | ||
return new Promise(function (resolve, reject) { | ||
var distclean = spawn(cmake, ['-E', 'remove_directory', buildDir], { | ||
stdio: 'inherit' | ||
}); | ||
function handleError(code) { | ||
if(code !== 0) { // An Error object will also not equal 0 | ||
var err = new Error('Unable to remove build directory'); | ||
err.code = 8; | ||
return reject(err); | ||
} | ||
return resolve(); | ||
} | ||
distclean.on('exit' , handleError); | ||
distclean.on('error', handleError); | ||
}); | ||
}, | ||
clean: function (argv, cmake) { | ||
// Run CMake clean if the project has been "configured" | ||
var args = ['--build', buildDir, '--target', 'clean']; | ||
args.push('--config', (argv.debug) ? 'Debug' : 'Release'); | ||
return new Promise(function (resolve, reject) { | ||
fs.exists(path.join(buildDir, 'CMakeCache.txt'), function (exists) { | ||
if(exists) { | ||
// Silently clean the project, do nothing on faiure (no-op) | ||
var clean = spawn(cmake, args, { | ||
stdio: 'ignore' | ||
}); | ||
function handleError(code) { return resolve(); } | ||
clean.on('exit', handleError); | ||
clean.on('error', handleError); | ||
} | ||
else resolve(); | ||
}); | ||
}); | ||
}, | ||
configure: function (argv, cmake) { | ||
var args = []; | ||
args.push('-DCMAKE_BUILD_TYPE=' + ((argv.debug) ? 'Debug' : 'Release')); | ||
if(argv.generator !== 'default') args.push('-G', argv.generator); | ||
if(argv.target) args.push('-DNODEJS_VERSION=' + argv.target); | ||
if(argv.distUrl) args.push('-DNODEJS_URL="' + argv.distUrl + '"'); | ||
if(argv.name) args.push('-DNODEJS_NAME="' + argv.name + '"'); | ||
args.push('..'); | ||
// Platform should match architecture on windows | ||
if(process.platform === 'win32') { | ||
if(conf.arch === 'ia32') conf.generator.arch = 'win32'; | ||
else conf.generator.arch = conf.arch; | ||
} | ||
return new Promise(function (resolve, reject) { | ||
// Use CMake as a cross-platform mkdir to create the build directory | ||
var mkdir = spawn(cmake, ['-E', 'make_directory', buildDir], { | ||
stdio: 'inherit' | ||
}); | ||
function handleError(code) { | ||
if(code !== 0) { | ||
var err = new Error('Unable to create build directory'); | ||
err.code = 3; | ||
return reject(err); | ||
} | ||
return resolve(); | ||
} | ||
mkdir.on('exit', handleError); | ||
mkdir.on('error', handleError); | ||
}).then(function () { | ||
return new Promise(function (resolve, reject) { | ||
// Run CMake to configure the project | ||
var configure = spawn(cmake, args, { | ||
cwd: path.resolve(buildDir), | ||
stdio: 'inherit' | ||
}); | ||
function handleError(code) { | ||
if(code !== 0) { | ||
var err = new Error('Unable to configure project'); | ||
err.code = 4; | ||
return reject(err); | ||
} | ||
return resolve(); | ||
} | ||
configure.on('exit', handleError); | ||
configure.on('error', handleError); | ||
}); | ||
}); | ||
}, | ||
build: function (argv, cmake) { | ||
// Run CMake build to build the project (generator agnostic) | ||
var args = ['--build', buildDir]; | ||
args.push('--config', (argv.debug) ? 'Debug' : 'Release'); | ||
return new Promise(function (resolve, reject) { | ||
fs.exists(path.join(buildDir, 'CMakeCache.txt'), function (exists) { | ||
if(exists) { | ||
var build = spawn(cmake, args, { | ||
stdio: 'inherit' | ||
}); | ||
function handleError(code) { | ||
if(code !== 0) { | ||
var err = new Error('Build failed'); | ||
err.code = 7; | ||
return reject(err); | ||
} | ||
return resolve(); | ||
} | ||
build.on('exit', handleError); | ||
build.on('error', handleError); | ||
} | ||
else { | ||
var err = new Error('Project is not configured, ' + | ||
'Run \'configure\' command first'); | ||
err.code = 6; | ||
return reject(err); | ||
} | ||
}); | ||
}); | ||
}, | ||
rebuild: function (argv, cmake) { | ||
// Per node-gyp, run clean, then configure, then build | ||
return commands.clean(argv, cmake) | ||
.then(function () { | ||
return commands.configure(argv, cmake); | ||
}) | ||
.then(function () { | ||
return commands.build(argv, cmake); | ||
}); | ||
}, | ||
install: deprecated, | ||
list: deprecated, | ||
remove: deprecated | ||
}; | ||
// Resolve the build location, and the path from there to the working directory | ||
var buildDir = path.resolve(path.join( | ||
conf.directory, conf.platform, conf.arch | ||
)); | ||
var buildRel = path.relative(buildDir, process.cwd()); | ||
// Load the configuration in the build directory | ||
var build = {}; | ||
// Find the cmake binary on the user's path | ||
var cmake; | ||
try { | ||
build = require(path.join(buildDir, 'build')); | ||
cmake = which.sync('cmake'); | ||
} | ||
catch(e) {} | ||
// Convert CMake booleans to actual boolean | ||
build.download = (build.download && ( | ||
build.download.toUpperCase() === 'ON' || | ||
build.download.toUpperCase() === 'TRUE' | ||
)); | ||
// Build the arguments set for cmake | ||
args = { | ||
generate: [], | ||
build: [] | ||
}; | ||
if(conf.generator.name) { | ||
args.generate.push('-G', conf.generator.name); | ||
catch(e) { | ||
console.error('CMake binary could not be found. Please verify your PATH.'); | ||
process.exit(127); | ||
} | ||
if(conf.generator.toolset) { | ||
args.generate.push('-T', conf.generator.toolset); | ||
} | ||
if(conf.generator.arch) { | ||
args.generate.push('-A', conf.generator.arch); | ||
} | ||
_.forEach(conf.flags, function buildFlag (value, flag) { | ||
args.generate.push('-D' + flag + '=' + value); | ||
}); | ||
args.generate.push(buildRel); | ||
args.build.push('--build', buildDir); | ||
if(conf.target) { | ||
args.build.push('--target', conf.target); | ||
} | ||
args.build.push('--config', conf.config); | ||
if(conf.clean) { | ||
args.build.push('--clean-first'); | ||
} | ||
if(conf.verbose) { | ||
args.build.push('--', conf.verbose); | ||
} | ||
// Workaround to build target architecture on Xcode, which doesnt respect | ||
// the platform generation (yet) | ||
if(conf.generator.name === 'Xcode') { | ||
var osxarch; | ||
if(conf.arch === 'x64') { | ||
osxarch = 'x86_64'; | ||
} | ||
else if(conf.arch === 'ia32') { | ||
osxarch = 'i386'; | ||
} | ||
else { | ||
console.log('Invalid architecture ' + conf.arch); | ||
process.exit(2); | ||
} | ||
args.build.push('--', '-arch', osxarch); | ||
// This script takes any number of options and a single command | ||
var argv = argparse.argv; | ||
if(argv._.length < 1) { | ||
console.error('A command must be specified.\n') | ||
argparse.showHelp(); | ||
process.exit(1); | ||
} | ||
function buildModule() { | ||
// Always create the build directory if necessary | ||
mkdirp(buildDir, function makeBuildDirectory (err) { | ||
if(err) throw err; | ||
var setupProc; | ||
var buildProc; | ||
function afterSetup () { | ||
buildProc = spawn(cmake, args.build, { stdio: 'inherit' }); | ||
buildProc.on('exit', function handleExitCodeDone (code) { | ||
process.exit(code); | ||
}); | ||
} | ||
// If we're not building, or the build configuration does not match the | ||
// current configuration, generate the project | ||
if(!conf.build || | ||
conf.config !== build.build_type || | ||
conf.variant && conf.variant !== build.variant || | ||
conf.version && conf.version !== build.version || | ||
!_.isUndefined(conf.download) && conf.download !== build.download || | ||
conf.standard && conf.standard !== build.standard) { | ||
setupProc = spawn(cmake, args.generate, { | ||
cwd: buildDir, stdio: 'inherit' | ||
}); | ||
setupProc.on('exit', function handleBadExitCode (code) { | ||
if(code !== 0) process.exit(code); | ||
}); | ||
} | ||
// Invoke the cmake build | ||
if(conf.build) { | ||
if(setupProc) { | ||
setupProc.on('exit', afterSetup); | ||
} | ||
else afterSetup(); | ||
} | ||
}); | ||
} | ||
// If the build changes in a way that is incompatible with the previous build | ||
// then remove the build directory (if it exists) before building | ||
// XXX: (conf.generator.arch && conf.generator.arch !== build.platform ) | ||
// Build now uses architecture specific folders | ||
if(!build.build_type || | ||
(conf.generator.name && conf.generator.name !== build.generator) || | ||
(conf.generator.toolset && conf.generator.toolset !== build.toolset )) { | ||
rimraf(buildDir, function afterRemove (err) { | ||
if(err) throw err; | ||
build = {}; | ||
buildModule(); | ||
}); | ||
} | ||
else buildModule(); | ||
// Parse the first plain-argument as the command to execute | ||
var cmd = argparse.argv._[0].toLowerCase(); | ||
var func = commands[cmd]; | ||
((func) ? func(argv, cmake) : Promise.reject( | ||
new Error('Invalid command \'' + cmd + '\'') | ||
)) | ||
// On success, exit cleanly | ||
.then(function () { | ||
process.exit(0); | ||
}) | ||
// Otherwise, log the error and exit with a status code | ||
.catch(function (err) { | ||
if(err instanceof Warning) console.warn(err.message); | ||
else console.error(err.message); | ||
process.exit(err.code || 2); | ||
}); |
@@ -1,2 +0,5 @@ | ||
Copyright (c) 2015, Colin Taylor | ||
(The ISC License) | ||
Copyright (c) 2015-2016, Colin Taylor <cjntaylor@gmail.com> | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
@@ -3,0 +6,0 @@ purpose with or without fee is hereby granted, provided that the above |
{ | ||
"name": "node-cmake", | ||
"author": "Colin Taylor <cjntaylor@gmail.com>", | ||
"version": "1.3.0", | ||
"version": "2.0.0", | ||
"description": "A CMake-based build system for node.js native modules", | ||
@@ -21,8 +21,5 @@ "keywords": [ | ||
"dependencies": { | ||
"lodash": "^4.8.2", | ||
"mkdirp": "^0.5.1", | ||
"mothership": "^0.3.0", | ||
"nan": "*", | ||
"rimraf": "^2.4.3", | ||
"which": "^1.1.2" | ||
"which": "^1.2.10", | ||
"yargs": "^5.0.0" | ||
}, | ||
@@ -29,0 +26,0 @@ "devDependencies": {}, |
341
README.md
Node CMake | ||
========== | ||
A CMake-based build system for node.js native modules, for CMake >= v3.1. | ||
A CMake-based build system for Node.js native modules, for CMake >= v3.1. | ||
## Motivation | ||
Newly rewritten for 2.0! | ||
The current build system for node native modules is | ||
[node-gyp](https://github.com/nodejs/node-gyp), which is based on the google | ||
project gyp. While this does provide a consistent build environment in | ||
line with the node build system, it has its own share of problems: | ||
##### New Features | ||
1. Cross platform library integration. As most node native modules are | ||
designed to expose some system-level interface to an existing library or | ||
framework, its very common to need to include other libraries and headers | ||
as dependencies of the module. Under gyp, these must be specified manually | ||
for each platform and architecture. | ||
* Drop-in execution compatibility with the `node-gyp` binary | ||
* Standardization with the `node-gyp` output and format. Should work better with tools that expect node-gyp's behaviour | ||
* Simplified, portable and customizable configuration script | ||
* Better recovery from failed downloads and builds | ||
* Generalized support for Node.js variants. This supports additional variant types that are compatible with the Node.js release server structure (SHAs defined for all resources hosted on the download server, headers and .lib files located in standard locations). | ||
2. Integration with other build systems. Gyp is very much its own system | ||
and doesn't play nice with existing builds. Common practice is to manually | ||
convert the existing build to gyp, adding developer overhead and complexity. | ||
While gyp is designed to generate build files, outside visual studio its | ||
support is limited in the IDE space. | ||
##### Removed Features | ||
3. Turing completeness. Gyp is a strictly declarative build system. Any | ||
conditional building is done through supported declarative syntax. For | ||
complicated builds, this adds a lot of complexity and overhead for something | ||
as simple as handing build conditions outside of architecture and platform ( | ||
Optional libraries, plugin based builds, nested conditions, etc.) | ||
* `find_package` support. The module loading techinque used in 1.0 was flawed for specific module structures and setup (deeply nested native dependencies). The new technique is to copy the now single configuration script into your project. This means that projects will not stay in sync with updates automatically. Be sure to copy the script again when updating `node-cmake` to a new version. | ||
* `ncmake` command line arguments. `ncmake` now follows and extends the `node-gyp` command line interface. Please update your build files to use the new syntax (`ncmake --build` => `ncmake rebuild`). For more info, see the [manual](docs/NcmakeManual.md). | ||
* Support for NW.js - The NW.js release server is not compliant to the Node.js release server structure. Workaround for the NW.js naming conventions have been included in the update, but the server SHA file does not include checksums for all resources, leaving no way to validate downloads. The SHASUMS256.txt file on the server needs to be updated to include these entries to be used with this tool. | ||
4. Depdencies. Gyp requires python to run, which is non-trival under windows, | ||
and is a constant source of frustration and configuration issues for those | ||
users. | ||
And probably the most important of all: | ||
4. Future stability. Google has rapidly been migrating v8 to | ||
[gn](https://chromium.googlesource.com/chromium/src/tools/gn/), leaving the | ||
future of gyp and its supportability questionable. Its clear that if gyp is | ||
to remain, node will likely have to take ownership of the project, as all of | ||
google's resources have been diverted to improving gn. Its also been made | ||
clear that gn isn't really intended to be used outside of chromium and isn't | ||
being exposed. Throw [bazel](http://bazel.io/) in the mix, and even gn's | ||
future is unstable. | ||
node-cmake addresses these concerns by providing a node module build system | ||
built on top of [CMake](https://cmake.org). CMake, like gyp, is a meta-build | ||
system, generating build files for the actual build tools and | ||
IDEs on each platform. It does this by using its own platform agnostic and | ||
turing complete syntax for describing the build process in general terms, | ||
and converting that syntax to the correct build instructions. | ||
CMake has long been a counter-argument and | ||
[competitor](https://gyp.gsrc.io/docs/GypVsCMake.md) | ||
to gyp. While the arguments outlined there are well reasoned, there are many | ||
arguments as to why CMake is good build system for cross platform builds: | ||
1. No dependencies. All that is required to use CMake is to install it, and | ||
whatever build environment is necessary for your platform (typically, | ||
visual studio / xcode / gcc). | ||
2. Library search. CMake provides a cross-platform and standardized way to | ||
find and link against external libraries, both those built with CMake and | ||
those built with other systems. It provides sensible defaults and looks in | ||
standard, correct places on each platform, and can be tuned for specific | ||
edge cases. If a library builds with CMake, their builds can be combined | ||
to build dependencies along with the main code. | ||
3. Stability and Integration. CMake has been in development far longer than | ||
gyp, and strives to maintain backwards compability across releases. Its an | ||
independent project that is used by a large number of big name projects | ||
and will be supported for a long time. It receives regular updates that | ||
improve and add functionality to make building more consistent, and to | ||
address changes to platform specific variations and complexity. It also | ||
nicely integrates with visual studio, xcode and especially IDEs like | ||
Jetbrain's CLion IDE and KDevelop, which use CMake as their native build | ||
environment. | ||
This project is similar in goals to | ||
[cmake-js](https://www.npmjs.com/package/cmake-js), with one major difference. | ||
Where that project has a node-centric focus, node-cmake relies strictly on | ||
CMake to download and configure node for building native extensions. This | ||
is done to support capabilites that are hard or impossible to integrate with | ||
`cmake-js`, namely toolchain support for cross-platform builds, and integration | ||
with other cmake projects and build environments (like catkin for ROS), which | ||
don't know about or potentially need a node interpreter to build. | ||
node-cmake is designed to provide full parity with `node-gyp` outside of | ||
the names and arguments of binaries; modules will still need to be updated to | ||
use it, but all of `node-gyp`'s capabilities are supported. | ||
## Usage | ||
To use this package, add the module `node-cmake` to your package.json | ||
as a development dependency: | ||
To use this package, add the module `node-cmake` to your package.json as a development dependency: | ||
npm install --save-dev node-cmake | ||
Then add a `CMakeLists.txt` file to the root of your module that contains | ||
the following at a minimum (\<REPLACE\> with your own definitions): | ||
Copy the file `NodeJS.cmake` from this module into your project. Then add a `CMakeLists.txt` file to the root of your module that contains at least the following (\<REPLACE\> with your own definitions): | ||
@@ -105,93 +33,35 @@ ```CMake | ||
project(<NAME OF MODULE>) | ||
project(<NAME OF MODULE> VERSION 1.0.0) | ||
list(APPEND CMAKE_MODULE_PATH | ||
${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-cmake | ||
) | ||
include(NodeJS.cmake) | ||
nodejs_init() | ||
find_package(NodeJS) | ||
add_nodejs_module(${PROJECT_NAME} <SOURCE AND HEADER FILES GO HERE>) | ||
``` | ||
The `CMakeLists.txt` file is the main build script for CMake, and has the | ||
same purpose as the `binding.gyp` file in `node-gyp`. | ||
This `CMakeLists.txt` file is the main build script for CMake, and replaces your existing `binding.gyp` file from `node-gyp`. It defines the build process for this project, including how to configure Node.js, and how to build your project components (modules, libraries, etc.) | ||
The `list(APPEND ...)` command tells CMake how to find the FindNodeJS.cmake | ||
script and must point to the directory containing this file. As written | ||
the variable `${CMAKE_CURRENT_SOURCE_DIR}` is a CMake variable referring to | ||
the directory that contains the `CMakeLists.txt` file being processed, or in | ||
this case, the root directory of your module. Since npm will install the | ||
`node-cmake` dependency underneath the `node_modules` directory, this tells | ||
CMake to look in the directory of this module for other scripts. | ||
The NodeJS.cmake file, included by relative path in this example, is a self-contained configuration tool for downloading the dependencies for building Node.js native modules, and configuring cmake to build them: | ||
The `find_package` command tells CMake that we want to find node as a | ||
dependency of our project. This command optionally takes arguments which | ||
are specified below. Typically, these arguments shouldn't be used unless | ||
necessary, to provide maximum flexibility for version and variant builds. | ||
`nodejs_init` configures the version of Node.js to use for building. By default, this is the currently installed version of node on your platform. This function can take a number of advanced options (see [Documentation](#documentation)). This function must be called before any other function provided by the script. | ||
Due to the complexity of creating a node module, and the strict requirements | ||
about the naming and placement of the shared library, node-cmake provides | ||
a CMake function for creating a node module, `add_nodejs_module`, similar to | ||
the `add_executable` and `add_library` commands native to CMake. This command | ||
ensures that the built shared library uses the correct build settings and | ||
flags on each platform. It creates a shared library `target`, specified by the | ||
first argument, that can be used identically to any other CMake `target`. In | ||
the example above, the target will be called '\<NAME OF MODULE\>' | ||
`add_nodejs_module` is a helper function for configuring CMake to build a node module based on the currently configured version of node. It takes the name of a `target` to create, along with a list of source files to build. All arguments after the `target` name are treated as sources. | ||
CMake has extensive documentation online, which can be | ||
found [here](https://cmake.org/documentation) for various versions of CMake. | ||
node-cmake REQUIRES CMake >= 3.1, but any newer version is also supported. | ||
CMake has extensive documentation online, which can be found [here](https://cmake.org/documentation). | ||
## Nan Support | ||
To simplify building of cross-version node modules, | ||
[Nan](https://github.com/nodejs/nan) is always included as a project | ||
dependency. In your module native sources, just | ||
```C++ | ||
#include <nan.h> | ||
``` | ||
At the top of any header/source file that requires this functionality. | ||
The version included with node-cmake will always be the newest available | ||
version of Nan. To use your own version, specify it as a dependency of your | ||
module: | ||
npm install --save-dev nan | ||
This version will be used instead of the dependency specified by node-cmake. | ||
## Building | ||
node-cmake can be built by invoking CMake with its usual syntax, optimally | ||
using an out-of-source build folder. This involves creating a folder, | ||
navigating to that directory, and then running the `cmake` executable, with | ||
an arugument to the folder containing the root `CMakeLists.txt`: | ||
node-cmake has been updated to provide a drop-in replacement for node-gyp's interface. To build your module, use the command `ncmake rebuild`, which cleans, configures and builds the module. | ||
mkdir build | ||
cd build | ||
cmake .. | ||
This can be integrated into npm via its "scripts" directive: | ||
To simplify integration with npm, this module provides an executable script | ||
called 'ncmake' that automates this process. Adding the following section: | ||
```JSON | ||
"scripts": { | ||
"install": "ncmake --build" | ||
"install": "ncmake rebuild" | ||
} | ||
``` | ||
to your `package.json` is typically all that is required to build. Additonal | ||
flags and options that can be passed to ncmake are outlined below. If you | ||
add additional non-optional arguments to this command, you MUST specify the | ||
build directory as the last argument (typically `out`). | ||
Once you've added this to your module, just run `npm install` to build. | ||
## Running | ||
node-cmake also provides a simple javascript module to simplify the | ||
finding and loading of the built native module in your own scripts. It exposes | ||
a single function similar to `require` with the same effects. Calling | ||
node-cmake also provides a simple javascript module to simplify the finding and loading of the built native module in your own scripts. It exposes a single function similar to `require` with the same effects. Calling | ||
@@ -202,155 +72,24 @@ ```JavaScript | ||
will return the native module to you if it can be found in the standard | ||
locations used by this module for building. This function handles loading | ||
the correct module for your platform and architecture, and searches both | ||
multiple build paths and mulitple configuration paths to find the | ||
native library. Additional options can be passed to this function to control | ||
these search parameters; see the `index.js` file in this directory for | ||
more information. | ||
will load and return the named native module by searching in standard build locations. A second boolean can be passed to this function to configure loading of debug builds by default (if available). | ||
## Variants | ||
node-cmake supports the concept of variants to allow building modules for | ||
alternate node environments, such as | ||
* [IO.js](https://iojs.org) - Now merged back into node (legacy support) | ||
* [NW.js](http://nwjs.io) | ||
* [Electron](http://electron.atom.io) | ||
These variants can be specified using their short names to `ncmake` using the | ||
`-s` flag. If a variant is specified, a version MUST also be specified using | ||
the `-v` flag. | ||
* Electron = electron | ||
* IOJS = iojs | ||
* NW.js = nw | ||
* Node = node | ||
Variants are stored in the `variants` folder and can be easily added or | ||
updated. If you are an owner of a variant and would like to request | ||
modifications or removal, or would like to add your own, please issue a | ||
pull request or feature request to that effect. | ||
## Versions | ||
Each variant maintains their own version range, which is handled properly | ||
by the variant. Versions are specified without the leading 'v' prefix, and | ||
must specify all three components: | ||
[MAJOR].[MINOR].[PATCH] | ||
Two additional version 'shortcuts' are supported, which | ||
have logical effects: | ||
* latest - Use the latest version for a variant. Requires downloading an | ||
additional file to determine this at runtime. | ||
* installed - Only applies to the default 'node' variant; uses the | ||
version of the running node interpreter. | ||
## Example | ||
An example project is provided in the `example` folder. Follow the directions | ||
in its README.md to build it correctly. | ||
An example project is provided in the `example` folder. Follow the directions in its README.md to build it correctly. | ||
## NCMake Manual | ||
## Documentation | ||
ncmake [options] [build_directory] | ||
* [ncmake Manual](docs/NcmakeManual.md) | ||
* [NodeJS.cmake Manual](docs/NodeJSCmakeManual.md) | ||
* [Nan support](docs/Nan.md) | ||
Create a build directory for CMake, run the CMake configuration, and | ||
optionally build the project. | ||
## Motivation | ||
This command can non-option arguments. If no non-option arguments are | ||
specified, the default build directory is assumed (out). If any | ||
non-option arguments are specified, the build directory relative to the | ||
current working directory MUST be specified as the last argument. | ||
This tool was developed as a replacement for [node-gyp](https://github.com/nodejs/node-gyp), the current Node.js build system. Google has stopped working on its core, the gyp build tool, to focus on its replacement [gn](https://chromium.googlesource.com/chromium/src/tools/gn) and the future of gyp is uncertain. | ||
Options (All case insensitive): | ||
CMake also provides a number of benfits over gyp: | ||
--config Set the build configuration. Can be one of 'MinSizeRel', | ||
'RelWithDebInfo', 'Release' or 'Debug'; or a custom | ||
CMake build type. See the CMake documentation for more | ||
information. | ||
Default: MinSizeRel | ||
--build Build the project in addition to configuration | ||
--clean-first Clean the project before building | ||
--target Build the specified target. Used for building specific | ||
modules, components and libraries. See the CMake | ||
documentation for more information. | ||
-C <STD> Set the C++ standard to <STD> level. Can be one of | ||
(98, 11, 14). | ||
Default: Unset, which uses the default level defined by CMake | ||
or the version required by the variant/version being built. | ||
-Z/-NZ Force the node sources required for building the module to | ||
be / not be downloaded. Useful if you want to use downloaded | ||
sources instead of the ones included on your platform. On | ||
some platforms/variants, sources must always be downloaded. | ||
-G <NAME> Set the CMake generator name. See the CMake documentation for | ||
more information. Used primarily on windows to specify the | ||
version of visual studio to build with (if not the default) | ||
Default: Unset | ||
-T <NAME> Set the toolset name for the generator. Used by some | ||
generators to configure the compiler used to build the | ||
sources. | ||
Default: Unset | ||
-S <VARIANT> Set the variant to build the module for. Used when | ||
building the module for variants other than Node.js | ||
Default: node | ||
-V <VERSION> Set the version of the variant to build against. Must | ||
be specified when the variant is specified. Can also | ||
be the special versions 'installed' and 'latest' when | ||
appropriate. | ||
Default: installed | ||
Advanced Options (All case insensitive): | ||
-A <ARCH> Set the target architecture for the node module. Useful | ||
when cross-compiling. Can be any valid output from | ||
'process.arch' | ||
Default: process.arch | ||
-P <PLATFORM> Set the target platform for the node module. Useful when | ||
cross-compiling. Can be any valid output from | ||
'process.platform' | ||
Default: process.platform | ||
## find_package Manual | ||
This is an advanced configuration and is primarily supported for exotic | ||
builds strictly in CMake. Generally, this functionality should not be used, | ||
relying on `ncmake` to build via npm. | ||
The NodeJS find_package command uses the VERSION and COMPONENTS arguments | ||
to support build-defined versions and variants. | ||
Specify the version immediately after `NodeJS` to hard-code a version | ||
requirement: | ||
```CMake | ||
find_package(NodeJS 0.12.7) | ||
``` | ||
To specify a variant, use ONE of the variant keywords above in all caps: | ||
```CMake | ||
find_package(NodeJS 0.12.3 COMPONENTS NWJS) | ||
``` | ||
The version must always be specified when using a component. | ||
* Does not depend on python | ||
* Extensive cross-platform utilities for finding and using other libraries and packages (the find_* family of commands, and the package system). | ||
* Native IDE support (Clion, KDevleop), and good support for platform IDEs (Visual Studio, Xcode, Eclipse) | ||
* Easy integration with other projects and build systems | ||
* Cross-compilation via toolchains |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
3
53080
18
290
93
4
2
+ Addedyargs@^5.0.0
+ Addedansi-regex@2.1.1(transitive)
+ Addedcamelcase@3.0.0(transitive)
+ Addedcliui@3.2.0(transitive)
+ Addedcode-point-at@1.1.0(transitive)
+ Addeddecamelize@1.2.0(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedfind-up@1.1.2(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-caller-file@1.0.3(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhosted-git-info@2.8.9(transitive)
+ Addedinvert-kv@1.0.0(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-core-module@2.16.1(transitive)
+ Addedis-fullwidth-code-point@1.0.0(transitive)
+ Addedis-utf8@0.2.1(transitive)
+ Addedlcid@1.0.0(transitive)
+ Addedload-json-file@1.1.0(transitive)
+ Addedlodash.assign@4.2.0(transitive)
+ Addednormalize-package-data@2.5.0(transitive)
+ Addednumber-is-nan@1.0.1(transitive)
+ Addedos-locale@1.4.0(transitive)
+ Addedparse-json@2.2.0(transitive)
+ Addedpath-exists@2.1.0(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpath-type@1.1.0(transitive)
+ Addedpify@2.3.0(transitive)
+ Addedpinkie@2.0.4(transitive)
+ Addedpinkie-promise@2.0.1(transitive)
+ Addedread-pkg@1.1.0(transitive)
+ Addedread-pkg-up@1.0.1(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedrequire-main-filename@1.0.1(transitive)
+ Addedresolve@1.22.10(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedspdx-correct@3.2.0(transitive)
+ Addedspdx-exceptions@2.5.0(transitive)
+ Addedspdx-expression-parse@3.0.1(transitive)
+ Addedspdx-license-ids@3.0.21(transitive)
+ Addedstring-width@1.0.2(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedstrip-bom@2.0.0(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedvalidate-npm-package-license@3.0.4(transitive)
+ Addedwhich-module@1.0.0(transitive)
+ Addedwindow-size@0.2.0(transitive)
+ Addedwrap-ansi@2.1.0(transitive)
+ Addedy18n@3.2.2(transitive)
+ Addedyargs@5.0.0(transitive)
+ Addedyargs-parser@3.2.0(transitive)
- Removedlodash@^4.8.2
- Removedmkdirp@^0.5.1
- Removedmothership@^0.3.0
- Removedrimraf@^2.4.3
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedfind-parent-dir@0.3.1(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedlodash@4.17.21(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedmothership@0.3.0(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedrimraf@2.7.1(transitive)
- Removedwrappy@1.0.2(transitive)
Updatedwhich@^1.2.10