check-napi.js 3.1 KB

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