bindings.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /**
  2. * Module dependencies.
  3. */
  4. var fs = require('fs'),
  5. path = require('path'),
  6. fileURLToPath = require('file-uri-to-path'),
  7. join = path.join,
  8. dirname = path.dirname,
  9. exists =
  10. (fs.accessSync &&
  11. function(path) {
  12. try {
  13. fs.accessSync(path);
  14. } catch (e) {
  15. return false;
  16. }
  17. return true;
  18. }) ||
  19. fs.existsSync ||
  20. path.existsSync,
  21. defaults = {
  22. arrow: process.env.NODE_BINDINGS_ARROW || ' → ',
  23. compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled',
  24. platform: process.platform,
  25. arch: process.arch,
  26. nodePreGyp:
  27. 'node-v' +
  28. process.versions.modules +
  29. '-' +
  30. process.platform +
  31. '-' +
  32. process.arch,
  33. version: process.versions.node,
  34. bindings: 'bindings.node',
  35. try: [
  36. // node-gyp's linked version in the "build" dir
  37. ['module_root', 'build', 'bindings'],
  38. // node-waf and gyp_addon (a.k.a node-gyp)
  39. ['module_root', 'build', 'Debug', 'bindings'],
  40. ['module_root', 'build', 'Release', 'bindings'],
  41. // Debug files, for development (legacy behavior, remove for node v0.9)
  42. ['module_root', 'out', 'Debug', 'bindings'],
  43. ['module_root', 'Debug', 'bindings'],
  44. // Release files, but manually compiled (legacy behavior, remove for node v0.9)
  45. ['module_root', 'out', 'Release', 'bindings'],
  46. ['module_root', 'Release', 'bindings'],
  47. // Legacy from node-waf, node <= 0.4.x
  48. ['module_root', 'build', 'default', 'bindings'],
  49. // Production "Release" buildtype binary (meh...)
  50. ['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'],
  51. // node-qbs builds
  52. ['module_root', 'addon-build', 'release', 'install-root', 'bindings'],
  53. ['module_root', 'addon-build', 'debug', 'install-root', 'bindings'],
  54. ['module_root', 'addon-build', 'default', 'install-root', 'bindings'],
  55. // node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
  56. ['module_root', 'lib', 'binding', 'nodePreGyp', 'bindings']
  57. ]
  58. };
  59. /**
  60. * The main `bindings()` function loads the compiled bindings for a given module.
  61. * It uses V8's Error API to determine the parent filename that this function is
  62. * being invoked from, which is then used to find the root directory.
  63. */
  64. function bindings(opts) {
  65. // Argument surgery
  66. if (typeof opts == 'string') {
  67. opts = { bindings: opts };
  68. } else if (!opts) {
  69. opts = {};
  70. }
  71. // maps `defaults` onto `opts` object
  72. Object.keys(defaults).map(function(i) {
  73. if (!(i in opts)) opts[i] = defaults[i];
  74. });
  75. // Get the module root
  76. if (!opts.module_root) {
  77. opts.module_root = exports.getRoot(exports.getFileName());
  78. }
  79. // Ensure the given bindings name ends with .node
  80. if (path.extname(opts.bindings) != '.node') {
  81. opts.bindings += '.node';
  82. }
  83. // https://github.com/webpack/webpack/issues/4175#issuecomment-342931035
  84. var requireFunc =
  85. typeof __webpack_require__ === 'function'
  86. ? __non_webpack_require__
  87. : require;
  88. var tries = [],
  89. i = 0,
  90. l = opts.try.length,
  91. n,
  92. b,
  93. err;
  94. for (; i < l; i++) {
  95. n = join.apply(
  96. null,
  97. opts.try[i].map(function(p) {
  98. return opts[p] || p;
  99. })
  100. );
  101. tries.push(n);
  102. try {
  103. b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
  104. if (!opts.path) {
  105. b.path = n;
  106. }
  107. return b;
  108. } catch (e) {
  109. if (e.code !== 'MODULE_NOT_FOUND' &&
  110. e.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' &&
  111. !/not find/i.test(e.message)) {
  112. throw e;
  113. }
  114. }
  115. }
  116. err = new Error(
  117. 'Could not locate the bindings file. Tried:\n' +
  118. tries
  119. .map(function(a) {
  120. return opts.arrow + a;
  121. })
  122. .join('\n')
  123. );
  124. err.tries = tries;
  125. throw err;
  126. }
  127. module.exports = exports = bindings;
  128. /**
  129. * Gets the filename of the JavaScript file that invokes this function.
  130. * Used to help find the root directory of a module.
  131. * Optionally accepts an filename argument to skip when searching for the invoking filename
  132. */
  133. exports.getFileName = function getFileName(calling_file) {
  134. var origPST = Error.prepareStackTrace,
  135. origSTL = Error.stackTraceLimit,
  136. dummy = {},
  137. fileName;
  138. Error.stackTraceLimit = 10;
  139. Error.prepareStackTrace = function(e, st) {
  140. for (var i = 0, l = st.length; i < l; i++) {
  141. fileName = st[i].getFileName();
  142. if (fileName !== __filename) {
  143. if (calling_file) {
  144. if (fileName !== calling_file) {
  145. return;
  146. }
  147. } else {
  148. return;
  149. }
  150. }
  151. }
  152. };
  153. // run the 'prepareStackTrace' function above
  154. Error.captureStackTrace(dummy);
  155. dummy.stack;
  156. // cleanup
  157. Error.prepareStackTrace = origPST;
  158. Error.stackTraceLimit = origSTL;
  159. // handle filename that starts with "file://"
  160. var fileSchema = 'file://';
  161. if (fileName.indexOf(fileSchema) === 0) {
  162. fileName = fileURLToPath(fileName);
  163. }
  164. return fileName;
  165. };
  166. /**
  167. * Gets the root directory of a module, given an arbitrary filename
  168. * somewhere in the module tree. The "root directory" is the directory
  169. * containing the `package.json` file.
  170. *
  171. * In: /home/nate/node-native-module/lib/index.js
  172. * Out: /home/nate/node-native-module
  173. */
  174. exports.getRoot = function getRoot(file) {
  175. var dir = dirname(file),
  176. prev;
  177. while (true) {
  178. if (dir === '.') {
  179. // Avoids an infinite loop in rare cases, like the REPL
  180. dir = process.cwd();
  181. }
  182. if (
  183. exists(join(dir, 'package.json')) ||
  184. exists(join(dir, 'node_modules'))
  185. ) {
  186. // Found the 'package.json' file or 'node_modules' dir; we're done
  187. return dir;
  188. }
  189. if (prev === dir) {
  190. // Got to the top
  191. throw new Error(
  192. 'Could not find module root given file: "' +
  193. file +
  194. '". Do you have a `package.json` file? '
  195. );
  196. }
  197. // Try the parent dir next
  198. prev = dir;
  199. dir = join(dir, '..');
  200. }
  201. };