├── LICENSE.md ├── README.md ├── bindings.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-bindings 2 | ============= 3 | ### Helper module for loading your native module's `.node` file 4 | 5 | This is a helper module for authors of Node.js native addon modules. 6 | It is basically the "swiss army knife" of `require()`ing your native module's 7 | `.node` file. 8 | 9 | Throughout the course of Node's native addon history, addons have ended up being 10 | compiled in a variety of different places, depending on which build tool and which 11 | version of node was used. To make matters worse, now the `gyp` build tool can 12 | produce either a __Release__ or __Debug__ build, each being built into different 13 | locations. 14 | 15 | This module checks _all_ the possible locations that a native addon would be built 16 | at, and returns the first one that loads successfully. 17 | 18 | 19 | Installation 20 | ------------ 21 | 22 | Install with `npm`: 23 | 24 | ``` bash 25 | $ npm install --save bindings 26 | ``` 27 | 28 | Or add it to the `"dependencies"` section of your `package.json` file. 29 | 30 | 31 | Example 32 | ------- 33 | 34 | `require()`ing the proper bindings file for the current node version, platform 35 | and architecture is as simple as: 36 | 37 | ``` js 38 | var bindings = require('bindings')('binding.node') 39 | 40 | // Use your bindings defined in your C files 41 | bindings.your_c_function() 42 | ``` 43 | 44 | 45 | Nice Error Output 46 | ----------------- 47 | 48 | When the `.node` file could not be loaded, `node-bindings` throws an Error with 49 | a nice error message telling you exactly what was tried. You can also check the 50 | `err.tries` Array property. 51 | 52 | ``` 53 | Error: Could not load the bindings file. Tried: 54 | → /Users/nrajlich/ref/build/binding.node 55 | → /Users/nrajlich/ref/build/Debug/binding.node 56 | → /Users/nrajlich/ref/build/Release/binding.node 57 | → /Users/nrajlich/ref/out/Debug/binding.node 58 | → /Users/nrajlich/ref/Debug/binding.node 59 | → /Users/nrajlich/ref/out/Release/binding.node 60 | → /Users/nrajlich/ref/Release/binding.node 61 | → /Users/nrajlich/ref/build/default/binding.node 62 | → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node 63 | at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13) 64 | at Object. (/Users/nrajlich/ref/lib/ref.js:5:47) 65 | at Module._compile (module.js:449:26) 66 | at Object.Module._extensions..js (module.js:467:10) 67 | at Module.load (module.js:356:32) 68 | at Function.Module._load (module.js:312:12) 69 | ... 70 | ``` 71 | 72 | The searching for the `.node` file will originate from the first directory in which has a `package.json` file is found. 73 | 74 | License 75 | ------- 76 | 77 | (The MIT License) 78 | 79 | Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining 82 | a copy of this software and associated documentation files (the 83 | 'Software'), to deal in the Software without restriction, including 84 | without limitation the rights to use, copy, modify, merge, publish, 85 | distribute, sublicense, and/or sell copies of the Software, and to 86 | permit persons to whom the Software is furnished to do so, subject to 87 | the following conditions: 88 | 89 | The above copyright notice and this permission notice shall be 90 | included in all copies or substantial portions of the Software. 91 | 92 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 93 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 94 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 95 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 96 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 97 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 98 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 99 | -------------------------------------------------------------------------------- /bindings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | fileURLToPath = require('file-uri-to-path'), 8 | join = path.join, 9 | dirname = path.dirname, 10 | exists = 11 | (fs.accessSync && 12 | function(path) { 13 | try { 14 | fs.accessSync(path); 15 | } catch (e) { 16 | return false; 17 | } 18 | return true; 19 | }) || 20 | fs.existsSync || 21 | path.existsSync, 22 | defaults = { 23 | arrow: process.env.NODE_BINDINGS_ARROW || ' → ', 24 | compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled', 25 | platform: process.platform, 26 | arch: process.arch, 27 | nodePreGyp: 28 | 'node-v' + 29 | process.versions.modules + 30 | '-' + 31 | process.platform + 32 | '-' + 33 | process.arch, 34 | version: process.versions.node, 35 | bindings: 'bindings.node', 36 | try: [ 37 | // node-gyp's linked version in the "build" dir 38 | ['module_root', 'build', 'bindings'], 39 | // node-waf and gyp_addon (a.k.a node-gyp) 40 | ['module_root', 'build', 'Debug', 'bindings'], 41 | ['module_root', 'build', 'Release', 'bindings'], 42 | // Debug files, for development (legacy behavior, remove for node v0.9) 43 | ['module_root', 'out', 'Debug', 'bindings'], 44 | ['module_root', 'Debug', 'bindings'], 45 | // Release files, but manually compiled (legacy behavior, remove for node v0.9) 46 | ['module_root', 'out', 'Release', 'bindings'], 47 | ['module_root', 'Release', 'bindings'], 48 | // Legacy from node-waf, node <= 0.4.x 49 | ['module_root', 'build', 'default', 'bindings'], 50 | // Production "Release" buildtype binary (meh...) 51 | ['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'], 52 | // node-qbs builds 53 | ['module_root', 'addon-build', 'release', 'install-root', 'bindings'], 54 | ['module_root', 'addon-build', 'debug', 'install-root', 'bindings'], 55 | ['module_root', 'addon-build', 'default', 'install-root', 'bindings'], 56 | // node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch} 57 | ['module_root', 'lib', 'binding', 'nodePreGyp', 'bindings'] 58 | ] 59 | }; 60 | 61 | /** 62 | * The main `bindings()` function loads the compiled bindings for a given module. 63 | * It uses V8's Error API to determine the parent filename that this function is 64 | * being invoked from, which is then used to find the root directory. 65 | */ 66 | 67 | function bindings(opts) { 68 | // Argument surgery 69 | if (typeof opts == 'string') { 70 | opts = { bindings: opts }; 71 | } else if (!opts) { 72 | opts = {}; 73 | } 74 | 75 | // maps `defaults` onto `opts` object 76 | Object.keys(defaults).map(function(i) { 77 | if (!(i in opts)) opts[i] = defaults[i]; 78 | }); 79 | 80 | // Get the module root 81 | if (!opts.module_root) { 82 | opts.module_root = exports.getRoot(exports.getFileName()); 83 | } 84 | 85 | // Ensure the given bindings name ends with .node 86 | if (path.extname(opts.bindings) != '.node') { 87 | opts.bindings += '.node'; 88 | } 89 | 90 | // https://github.com/webpack/webpack/issues/4175#issuecomment-342931035 91 | var requireFunc = 92 | typeof __webpack_require__ === 'function' 93 | ? __non_webpack_require__ 94 | : require; 95 | 96 | var tries = [], 97 | i = 0, 98 | l = opts.try.length, 99 | n, 100 | b, 101 | err; 102 | 103 | for (; i < l; i++) { 104 | n = join.apply( 105 | null, 106 | opts.try[i].map(function(p) { 107 | return opts[p] || p; 108 | }) 109 | ); 110 | tries.push(n); 111 | try { 112 | b = opts.path ? requireFunc.resolve(n) : requireFunc(n); 113 | if (!opts.path) { 114 | b.path = n; 115 | } 116 | return b; 117 | } catch (e) { 118 | if ( 119 | e.code !== 'MODULE_NOT_FOUND' && 120 | e.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' && 121 | !/not find/i.test(e.message) 122 | ) { 123 | throw e; 124 | } 125 | } 126 | } 127 | 128 | err = new Error( 129 | 'Could not locate the bindings file. Tried:\n' + 130 | tries 131 | .map(function(a) { 132 | return opts.arrow + a; 133 | }) 134 | .join('\n') 135 | ); 136 | err.tries = tries; 137 | throw err; 138 | } 139 | module.exports = exports = bindings; 140 | 141 | /** 142 | * Gets the filename of the JavaScript file that invokes this function. 143 | * Used to help find the root directory of a module. 144 | * Optionally accepts an filename argument to skip when searching for the invoking filename 145 | */ 146 | 147 | exports.getFileName = function getFileName(calling_file) { 148 | var origPST = Error.prepareStackTrace, 149 | origSTL = Error.stackTraceLimit, 150 | dummy = {}, 151 | fileName; 152 | 153 | Error.stackTraceLimit = 10; 154 | 155 | Error.prepareStackTrace = function(e, st) { 156 | for (var i = 0, l = st.length; i < l; i++) { 157 | fileName = st[i].getFileName(); 158 | if (fileName !== __filename) { 159 | if (calling_file) { 160 | if (fileName !== calling_file) { 161 | return; 162 | } 163 | } else { 164 | return; 165 | } 166 | } 167 | } 168 | }; 169 | 170 | // run the 'prepareStackTrace' function above 171 | Error.captureStackTrace(dummy); 172 | dummy.stack; 173 | 174 | // cleanup 175 | Error.prepareStackTrace = origPST; 176 | Error.stackTraceLimit = origSTL; 177 | 178 | // handle filename that starts with "file://" 179 | var fileSchema = 'file://'; 180 | if (fileName.indexOf(fileSchema) === 0) { 181 | fileName = fileURLToPath(fileName); 182 | } 183 | 184 | return fileName; 185 | }; 186 | 187 | /** 188 | * Gets the root directory of a module, given an arbitrary filename 189 | * somewhere in the module tree. The "root directory" is the directory 190 | * containing the `package.json` file. 191 | * 192 | * In: /home/nate/node-native-module/lib/index.js 193 | * Out: /home/nate/node-native-module 194 | */ 195 | 196 | exports.getRoot = function getRoot(file) { 197 | var dir = dirname(file), 198 | prev; 199 | while (true) { 200 | if (dir === '.') { 201 | // Avoids an infinite loop in rare cases, like the REPL 202 | dir = process.cwd(); 203 | } 204 | if ( 205 | exists(join(dir, 'package.json')) || 206 | exists(join(dir, 'node_modules')) 207 | ) { 208 | // Found the 'package.json' file or 'node_modules' dir; we're done 209 | return dir; 210 | } 211 | if (prev === dir) { 212 | // Got to the top 213 | throw new Error( 214 | 'Could not find module root given file: "' + 215 | file + 216 | '". Do you have a `package.json` file? ' 217 | ); 218 | } 219 | // Try the parent dir next 220 | prev = dir; 221 | dir = join(dir, '..'); 222 | } 223 | }; 224 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bindings", 3 | "description": "Helper module for loading your native module's .node file", 4 | "keywords": [ 5 | "native", 6 | "addon", 7 | "bindings", 8 | "gyp", 9 | "waf", 10 | "c", 11 | "c++" 12 | ], 13 | "version": "1.5.0", 14 | "author": "Nathan Rajlich (http://tootallnate.net)", 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/TooTallNate/node-bindings.git" 18 | }, 19 | "main": "./bindings.js", 20 | "bugs": { 21 | "url": "https://github.com/TooTallNate/node-bindings/issues" 22 | }, 23 | "homepage": "https://github.com/TooTallNate/node-bindings", 24 | "license": "MIT", 25 | "dependencies": { 26 | "file-uri-to-path": "1.0.0" 27 | } 28 | } 29 | --------------------------------------------------------------------------------