module.exports = exports; var path = require('path') , semver = require('semver') , url = require('url') , abi_crosswalk = require('./abi_crosswalk.json') function get_node_webkit_abi(runtime, target_version) { if (!runtime) { throw new Error("get_node_webkit_abi requires valid runtime arg"); } if (typeof target_version === 'undefined') { // erroneous CLI call throw new Error("Empty target version is not supported if node-webkit is the target."); } return runtime + '-v' + target_version; } module.exports.get_node_webkit_abi = get_node_webkit_abi; function get_node_abi(runtime, versions) { if (!runtime) { throw new Error("get_node_abi requires valid runtime arg"); } if (!versions) { throw new Error("get_node_abi requires valid process.versions object"); } var sem_ver = semver.parse(versions.node); if (sem_ver.major === 0 && sem_ver.minor % 2) { // odd series // https://github.com/mapbox/node-pre-gyp/issues/124 return runtime+'-v'+versions.node; } else { // process.versions.modules added in >= v0.10.4 and v0.11.7 // https://github.com/joyent/node/commit/ccabd4a6fa8a6eb79d29bc3bbe9fe2b6531c2d8e return versions.modules ? runtime+'-v' + (+versions.modules) : 'v8-' + versions.v8.split('.').slice(0,2).join('.'); } } module.exports.get_node_abi = get_node_abi; function get_runtime_abi(runtime, target_version) { if (!runtime) { throw new Error("get_runtime_abi requires valid runtime arg"); } if (runtime === 'node-webkit') { var target_version = target_version || process.versions['node-webkit']; return get_node_webkit_abi(runtime, target_version); } else { if (runtime != 'node') { throw new Error("Unknown Runtime: '" + runtime + "'"); } if (!target_version) { return get_node_abi(runtime,process.versions); } else { var cross_obj; // abi_crosswalk generated with ./scripts/abi_crosswalk.js if (abi_crosswalk[target_version]) { cross_obj = abi_crosswalk[target_version]; } else { var target_parts = target_version.split('.').map(function(i) { return +i }); if (target_parts.length == 3 && (target_parts[1] % 2 == 0)) { var patch = target_parts[2]; while (--patch > 0) { var new_target = '' + target_parts[0] + '.' + target_parts[1] + '.' + patch; if (abi_crosswalk[new_target]) { cross_obj = abi_crosswalk[target_version]; break; } } } } if (!cross_obj) { throw new Error("Unsupported target version: " + target_version); } // emulate process.versions var versions_obj = { node: target_version, v8: cross_obj.v8+'.0', // abi_crosswalk uses 1 for node versions lacking process.versions.modules // process.versions.modules added in >= v0.10.4 and v0.11.7 modules: cross_obj.node_abi > 1 ? cross_obj.node_abi : undefined } return get_node_abi(runtime, versions_obj); } } } module.exports.get_runtime_abi = get_runtime_abi; var required_parameters = [ 'module_name', 'module_path', 'host' ]; function validate_config(package_json,callback) { var msg = package_json.name + ' package.json is not node-pre-gyp ready:\n'; var missing = []; if (!package_json.main) { missing.push('main'); } if (!package_json.version) { missing.push('version'); } if (!package_json.name) { missing.push('name'); } if (!package_json.binary) { missing.push('binary'); } var o = package_json.binary; required_parameters.forEach(function(p) { if (missing.indexOf('binary') > -1) { missing.pop('binary'); } if (!o || o[p] == undefined) { missing.push('binary.' + p); } }); if (missing.length >= 1) { throw new Error(msg+"package.json must declare these properties: \n" + missing.join('\n')); } if (o) { // enforce https over http var protocol = url.parse(o.host).protocol; if (protocol === 'http:') { throw new Error("'host' protocol ("+protocol+") is invalid - only 'https:' is accepted"); } } }; module.exports.validate_config = validate_config; function eval_template(template,opts) { Object.keys(opts).forEach(function(key) { var pattern = '{'+key+'}'; while (template.indexOf(pattern) > -1) { template = template.replace(pattern,opts[key]); } }); return template; } // url.resolve needs single trailing slash // to behave correctly, otherwise a double slash // may end up in the url which breaks requests // and a lacking slash may not lead to proper joining function fix_slashes(pathname) { if (pathname.slice(-1) != '/') { return pathname + '/'; } return pathname; } // remove double slashes // note: path.normalize will not work because // it will convert forward to back slashes function drop_double_slashes(pathname) { return pathname.replace(/\/\//g,'/'); } var default_package_name = '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz'; var default_remote_path = ''; module.exports.evaluate = function(package_json,options) { options = options || {}; validate_config(package_json); var v = package_json.version; var module_version = semver.parse(v); var runtime = options.runtime || (process.versions['node-webkit'] ? 'node-webkit' : 'node'); var opts = { name: package_json.name , configuration: Boolean(options.debug) ? 'Debug' : 'Release' , debug: options.debug , module_name: package_json.binary.module_name , version: module_version.version , prerelease: module_version.prerelease.length ? module_version.prerelease.join('.') : '' , build: module_version.build.length ? module_version.build.join('.') : '' , major: module_version.major , minor: module_version.minor , patch: module_version.patch , runtime: runtime , node_abi: get_runtime_abi(runtime,options.target) , target: options.target || '' , platform: options.target_platform || process.platform , target_platform: options.target_platform || process.platform , arch: options.target_arch || process.arch , target_arch: options.target_arch || process.arch , module_main: package_json.main , toolset : options.toolset || '' // address https://github.com/mapbox/node-pre-gyp/issues/119 } opts.host = fix_slashes(eval_template(package_json.binary.host,opts)); opts.module_path = eval_template(package_json.binary.module_path,opts); // now we resolve the module_path to ensure it is absolute so that binding.gyp variables work predictably if (options.module_root) { // resolve relative to known module root: works for pre-binding require opts.module_path = path.join(options.module_root,opts.module_path); } else { // resolve relative to current working directory: works for node-pre-gyp commands opts.module_path = path.resolve(opts.module_path); } opts.module = path.join(opts.module_path,opts.module_name + '.node') opts.remote_path = package_json.binary.remote_path ? drop_double_slashes(fix_slashes(eval_template(package_json.binary.remote_path,opts))) : default_remote_path; var package_name = package_json.binary.package_name ? package_json.binary.package_name : default_package_name; opts.package_name = eval_template(package_name,opts); opts.staged_tarball = path.join('build/stage',opts.remote_path,opts.package_name); opts.hosted_path = url.resolve(opts.host,opts.remote_path); opts.hosted_tarball = url.resolve(opts.hosted_path,opts.package_name); return opts; }