Skip to content

Commit fd3c37b

Browse files
Gabriel Schulhofmhdawson
authored andcommitted
tools: add tool to check for N-API modules
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>
1 parent fc11c94 commit fd3c37b

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ to ideas specified in the **ECMA262 Language Specification**.
5757
- [node-gyp](doc/node-gyp.md)
5858
- [cmake-js](doc/cmake-js.md)
5959
- [Conversion tool](doc/conversion-tool.md)
60+
- [Checker tool](doc/checker-tool.md)
6061
- [Generator](doc/generator.md)
6162

6263
<a name="api"></a>

doc/checker-tool.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Checker Tool
2+
3+
**node-addon-api** provides a [checker tool][] that will inspect a given
4+
directory tree, identifying all Node.js native addons therein, and further
5+
indicating for each addon whether it is an N-API addon.
6+
7+
## To use the checker tool:
8+
9+
1. Install the application with `npm install`.
10+
11+
2. If the application does not depend on **node-addon-api**, copy the
12+
checker tool into the application's directory.
13+
14+
3. If the application does not depend on **node-addon-api**, run the checker
15+
tool from the application's directory:
16+
17+
```sh
18+
node ./check-napi.js
19+
```
20+
21+
Otherwise, the checker tool can be run from the application's
22+
`node_modules/` subdirectory:
23+
24+
```sh
25+
node ./node_modules/node-addon-api/tools/check-napi.js
26+
```
27+
28+
The tool accepts the root directory from which to start checking for Node.js
29+
native addons as a single optional command line parameter. If ommitted it will
30+
start checking from the current directory (`.`).
31+
32+
[checker tool]: ../tools/check-napi.js

tools/check-napi.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use strict';
2+
// Descend into a directory structure and, for each file matching *.node, output
3+
// based on the imports found in the file whether it's an N-API module or not.
4+
5+
const fs = require('fs');
6+
const path = require('path');
7+
const child_process = require('child_process');
8+
9+
// Read the output of the command, break it into lines, and use the reducer to
10+
// decide whether the file is an N-API module or not.
11+
function checkFile(file, command, argv, reducer) {
12+
const child = child_process.spawn(command, argv, {
13+
stdio: ['inherit', 'pipe', 'inherit']
14+
});
15+
let leftover = '';
16+
let isNapi = undefined;
17+
child.stdout.on('data', (chunk) => {
18+
if (isNapi === undefined) {
19+
chunk = (leftover + chunk.toString()).split(/[\r\n]+/);
20+
leftover = chunk.pop();
21+
isNapi = chunk.reduce(reducer, isNapi);
22+
if (isNapi !== undefined) {
23+
child.kill();
24+
}
25+
}
26+
});
27+
child.on('close', (code, signal) => {
28+
if ((code === null && signal !== null) || (code !== 0)) {
29+
console.log(
30+
command + ' exited with code: ' + code + ' and signal: ' + signal);
31+
} else {
32+
// Green if it's a N-API module, red otherwise.
33+
console.log(
34+
'\x1b[' + (isNapi ? '42' : '41') + 'm' +
35+
(isNapi ? ' N-API' : 'Not N-API') +
36+
'\x1b[0m: ' + file);
37+
}
38+
});
39+
}
40+
41+
// Use nm -a to list symbols.
42+
function checkFileUNIX(file) {
43+
checkFile(file, 'nm', ['-a', file], (soFar, line) => {
44+
if (soFar === undefined) {
45+
line = line.match(/([0-9a-f]*)? ([a-zA-Z]) (.*$)/);
46+
if (line[2] === 'U') {
47+
if (/^napi/.test(line[3])) {
48+
soFar = true;
49+
}
50+
}
51+
}
52+
return soFar;
53+
});
54+
}
55+
56+
// Use dumpbin /imports to list symbols.
57+
function checkFileWin32(file) {
58+
checkFile(file, 'dumpbin', ['/imports', file], (soFar, line) => {
59+
if (soFar === undefined) {
60+
line = line.match(/([0-9a-f]*)? +([a-zA-Z0-9]) (.*$)/);
61+
if (line && /^napi/.test(line[line.length - 1])) {
62+
soFar = true;
63+
}
64+
}
65+
return soFar;
66+
});
67+
}
68+
69+
// Descend into a directory structure and pass each file ending in '.node' to
70+
// one of the above checks, depending on the OS.
71+
function recurse(top) {
72+
fs.readdir(top, (error, items) => {
73+
if (error) {
74+
throw ("error reading directory " + top + ": " + error);
75+
}
76+
items.forEach((item) => {
77+
item = path.join(top, item);
78+
fs.stat(item, ((item) => (error, stats) => {
79+
if (error) {
80+
throw ("error about " + item + ": " + error);
81+
}
82+
if (stats.isDirectory()) {
83+
recurse(item);
84+
} else if (/[.]node$/.test(item) &&
85+
// Explicitly ignore files called 'nothing.node' because they are
86+
// artefacts of node-addon-api having identified a version of
87+
// Node.js that ships with a correct implementation of N-API.
88+
path.basename(item) !== 'nothing.node') {
89+
process.platform === 'win32' ?
90+
checkFileWin32(item) :
91+
checkFileUNIX(item);
92+
}
93+
})(item));
94+
});
95+
});
96+
}
97+
98+
// Start with the directory given on the command line or the current directory
99+
// if nothing was given.
100+
recurse(process.argv.length > 3 ? process.argv[2] : '.');

0 commit comments

Comments
 (0)