bindings.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 (!/not find/i.test(e.message)) {
  110. throw e;
  111. }
  112. }
  113. }
  114. err = new Error(
  115. 'Could not locate the bindings file. Tried:\n' +
  116. tries
  117. .map(function(a) {
  118. return opts.arrow + a;
  119. })
  120. .join('\n')
  121. );
  122. err.tries = tries;
  123. throw err;
  124. }
  125. module.exports = exports = bindings;
  126. /**
  127. * Gets the filename of the JavaScript file that invokes this function.
  128. * Used to help find the root directory of a module.
  129. * Optionally accepts an filename argument to skip when searching for the invoking filename
  130. */
  131. exports.getFileName = function getFileName(calling_file) {
  132. var origPST = Error.prepareStackTrace,
  133. origSTL = Error.stackTraceLimit,
  134. dummy = {},
  135. fileName;
  136. Error.stackTraceLimit = 10;
  137. Error.prepareStackTrace = function(e, st) {
  138. for (var i = 0, l = st.length; i < l; i++) {
  139. fileName = st[i].getFileName();
  140. if (fileName !== __filename) {
  141. if (calling_file) {
  142. if (fileName !== calling_file) {
  143. return;
  144. }
  145. } else {
  146. return;
  147. }
  148. }
  149. }
  150. };
  151. // run the 'prepareStackTrace' function above
  152. Error.captureStackTrace(dummy);
  153. dummy.stack;
  154. // cleanup
  155. Error.prepareStackTrace = origPST;
  156. Error.stackTraceLimit = origSTL;
  157. // handle filename that starts with "file://"
  158. var fileSchema = 'file://';
  159. if (fileName.indexOf(fileSchema) === 0) {
  160. fileName = fileURLToPath(fileName);
  161. }
  162. return fileName;
  163. };
  164. /**
  165. * Gets the root directory of a module, given an arbitrary filename
  166. * somewhere in the module tree. The "root directory" is the directory
  167. * containing the `package.json` file.
  168. *
  169. * In: /home/nate/node-native-module/lib/index.js
  170. * Out: /home/nate/node-native-module
  171. */
  172. exports.getRoot = function getRoot(file) {
  173. var dir = dirname(file),
  174. prev;
  175. while (true) {
  176. if (dir === '.') {
  177. // Avoids an infinite loop in rare cases, like the REPL
  178. dir = process.cwd();
  179. }
  180. if (
  181. exists(join(dir, 'package.json')) ||
  182. exists(join(dir, 'node_modules'))
  183. ) {
  184. // Found the 'package.json' file or 'node_modules' dir; we're done
  185. return dir;
  186. }
  187. if (prev === dir) {
  188. // Got to the top
  189. throw new Error(
  190. 'Could not find module root given file: "' +
  191. file +
  192. '". Do you have a `package.json` file? '
  193. );
  194. }
  195. // Try the parent dir next
  196. prev = dir;
  197. dir = join(dir, '..');
  198. }
  199. };