Skip to content

Commit

Permalink
tools: add tool to check for N-API modules
Browse files Browse the repository at this point in the history
Adds tools/check-napi.js which uses `nm -a` on UNIX and
`dumpbin /imports` on Windows to check whether a given `.node` file
is an N-API module or not. Intentionally ignores files named
`nothing.node` because they are node-addon-api build artefacts.

Sets the target type for `nothing` (which gets built when a built-in
N-API is found to be present) to `'static_library'` so as to avoid the
creation of `nothing.node` files which incorrectly end up showing up in
the output of `check-napi.js` as non-N-API modules.

PR-URL: #346
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
  • Loading branch information
Gabriel Schulhof authored and mhdawson committed Sep 21, 2018
1 parent fc11c94 commit fd3c37b
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ to ideas specified in the **ECMA262 Language Specification**.
- [node-gyp](doc/node-gyp.md)
- [cmake-js](doc/cmake-js.md)
- [Conversion tool](doc/conversion-tool.md)
- [Checker tool](doc/checker-tool.md)
- [Generator](doc/generator.md)

<a name="api"></a>
Expand Down
32 changes: 32 additions & 0 deletions doc/checker-tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Checker Tool

**node-addon-api** provides a [checker tool][] that will inspect a given
directory tree, identifying all Node.js native addons therein, and further
indicating for each addon whether it is an N-API addon.

## To use the checker tool:

1. Install the application with `npm install`.

2. If the application does not depend on **node-addon-api**, copy the
checker tool into the application's directory.

3. If the application does not depend on **node-addon-api**, run the checker
tool from the application's directory:

```sh
node ./check-napi.js
```

Otherwise, the checker tool can be run from the application's
`node_modules/` subdirectory:
```sh
node ./node_modules/node-addon-api/tools/check-napi.js
```
The tool accepts the root directory from which to start checking for Node.js
native addons as a single optional command line parameter. If ommitted it will
start checking from the current directory (`.`).
[checker tool]: ../tools/check-napi.js
100 changes: 100 additions & 0 deletions tools/check-napi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use strict';
// Descend into a directory structure and, for each file matching *.node, output
// based on the imports found in the file whether it's an N-API module or not.

const fs = require('fs');
const path = require('path');
const child_process = require('child_process');

// Read the output of the command, break it into lines, and use the reducer to
// decide whether the file is an N-API module or not.
function checkFile(file, command, argv, reducer) {
const child = child_process.spawn(command, argv, {
stdio: ['inherit', 'pipe', 'inherit']
});
let leftover = '';
let isNapi = undefined;
child.stdout.on('data', (chunk) => {
if (isNapi === undefined) {
chunk = (leftover + chunk.toString()).split(/[\r\n]+/);
leftover = chunk.pop();
isNapi = chunk.reduce(reducer, isNapi);
if (isNapi !== undefined) {
child.kill();
}
}
});
child.on('close', (code, signal) => {
if ((code === null && signal !== null) || (code !== 0)) {
console.log(
command + ' exited with code: ' + code + ' and signal: ' + signal);
} else {
// Green if it's a N-API module, red otherwise.
console.log(
'\x1b[' + (isNapi ? '42' : '41') + 'm' +
(isNapi ? ' N-API' : 'Not N-API') +
'\x1b[0m: ' + file);
}
});
}

// Use nm -a to list symbols.
function checkFileUNIX(file) {
checkFile(file, 'nm', ['-a', file], (soFar, line) => {
if (soFar === undefined) {
line = line.match(/([0-9a-f]*)? ([a-zA-Z]) (.*$)/);
if (line[2] === 'U') {
if (/^napi/.test(line[3])) {
soFar = true;
}
}
}
return soFar;
});
}

// Use dumpbin /imports to list symbols.
function checkFileWin32(file) {
checkFile(file, 'dumpbin', ['/imports', file], (soFar, line) => {
if (soFar === undefined) {
line = line.match(/([0-9a-f]*)? +([a-zA-Z0-9]) (.*$)/);
if (line && /^napi/.test(line[line.length - 1])) {
soFar = true;
}
}
return soFar;
});
}

// Descend into a directory structure and pass each file ending in '.node' to
// one of the above checks, depending on the OS.
function recurse(top) {
fs.readdir(top, (error, items) => {
if (error) {
throw ("error reading directory " + top + ": " + error);
}
items.forEach((item) => {
item = path.join(top, item);
fs.stat(item, ((item) => (error, stats) => {
if (error) {
throw ("error about " + item + ": " + error);
}
if (stats.isDirectory()) {
recurse(item);
} else if (/[.]node$/.test(item) &&
// Explicitly ignore files called 'nothing.node' because they are
// artefacts of node-addon-api having identified a version of
// Node.js that ships with a correct implementation of N-API.
path.basename(item) !== 'nothing.node') {
process.platform === 'win32' ?
checkFileWin32(item) :
checkFileUNIX(item);
}
})(item));
});
});
}

// Start with the directory given on the command line or the current directory
// if nothing was given.
recurse(process.argv.length > 3 ? process.argv[2] : '.');

0 comments on commit fd3c37b

Please sign in to comment.