bindings.js 4.7 KB

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