You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.9 KiB
207 lines
5.9 KiB
var fs = require('fs') |
|
var path = require('path') |
|
var os = require('os') |
|
|
|
// Workaround to fix webpack's build warnings: 'the request of a dependency is an expression' |
|
var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line |
|
|
|
var vars = (process.config && process.config.variables) || {} |
|
var prebuildsOnly = !!process.env.PREBUILDS_ONLY |
|
var abi = process.versions.modules // TODO: support old node where this is undef |
|
var runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node') |
|
|
|
var arch = process.env.npm_config_arch || os.arch() |
|
var platform = process.env.npm_config_platform || os.platform() |
|
var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') |
|
var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || '' |
|
var uv = (process.versions.uv || '').split('.')[0] |
|
|
|
module.exports = load |
|
|
|
function load (dir) { |
|
return runtimeRequire(load.resolve(dir)) |
|
} |
|
|
|
load.resolve = load.path = function (dir) { |
|
dir = path.resolve(dir || '.') |
|
|
|
try { |
|
var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_') |
|
if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD'] |
|
} catch (err) {} |
|
|
|
if (!prebuildsOnly) { |
|
var release = getFirst(path.join(dir, 'build/Release'), matchBuild) |
|
if (release) return release |
|
|
|
var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild) |
|
if (debug) return debug |
|
} |
|
|
|
var prebuild = resolve(dir) |
|
if (prebuild) return prebuild |
|
|
|
var nearby = resolve(path.dirname(process.execPath)) |
|
if (nearby) return nearby |
|
|
|
var target = [ |
|
'platform=' + platform, |
|
'arch=' + arch, |
|
'runtime=' + runtime, |
|
'abi=' + abi, |
|
'uv=' + uv, |
|
armv ? 'armv=' + armv : '', |
|
'libc=' + libc, |
|
'node=' + process.versions.node, |
|
process.versions.electron ? 'electron=' + process.versions.electron : '', |
|
typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line |
|
].filter(Boolean).join(' ') |
|
|
|
throw new Error('No native build was found for ' + target + '\n loaded from: ' + dir + '\n') |
|
|
|
function resolve (dir) { |
|
// Find matching "prebuilds/<platform>-<arch>" directory |
|
var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple) |
|
var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0] |
|
if (!tuple) return |
|
|
|
// Find most specific flavor first |
|
var prebuilds = path.join(dir, 'prebuilds', tuple.name) |
|
var parsed = readdirSync(prebuilds).map(parseTags) |
|
var candidates = parsed.filter(matchTags(runtime, abi)) |
|
var winner = candidates.sort(compareTags(runtime))[0] |
|
if (winner) return path.join(prebuilds, winner.file) |
|
} |
|
} |
|
|
|
function readdirSync (dir) { |
|
try { |
|
return fs.readdirSync(dir) |
|
} catch (err) { |
|
return [] |
|
} |
|
} |
|
|
|
function getFirst (dir, filter) { |
|
var files = readdirSync(dir).filter(filter) |
|
return files[0] && path.join(dir, files[0]) |
|
} |
|
|
|
function matchBuild (name) { |
|
return /\.node$/.test(name) |
|
} |
|
|
|
function parseTuple (name) { |
|
// Example: darwin-x64+arm64 |
|
var arr = name.split('-') |
|
if (arr.length !== 2) return |
|
|
|
var platform = arr[0] |
|
var architectures = arr[1].split('+') |
|
|
|
if (!platform) return |
|
if (!architectures.length) return |
|
if (!architectures.every(Boolean)) return |
|
|
|
return { name, platform, architectures } |
|
} |
|
|
|
function matchTuple (platform, arch) { |
|
return function (tuple) { |
|
if (tuple == null) return false |
|
if (tuple.platform !== platform) return false |
|
return tuple.architectures.includes(arch) |
|
} |
|
} |
|
|
|
function compareTuples (a, b) { |
|
// Prefer single-arch prebuilds over multi-arch |
|
return a.architectures.length - b.architectures.length |
|
} |
|
|
|
function parseTags (file) { |
|
var arr = file.split('.') |
|
var extension = arr.pop() |
|
var tags = { file: file, specificity: 0 } |
|
|
|
if (extension !== 'node') return |
|
|
|
for (var i = 0; i < arr.length; i++) { |
|
var tag = arr[i] |
|
|
|
if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') { |
|
tags.runtime = tag |
|
} else if (tag === 'napi') { |
|
tags.napi = true |
|
} else if (tag.slice(0, 3) === 'abi') { |
|
tags.abi = tag.slice(3) |
|
} else if (tag.slice(0, 2) === 'uv') { |
|
tags.uv = tag.slice(2) |
|
} else if (tag.slice(0, 4) === 'armv') { |
|
tags.armv = tag.slice(4) |
|
} else if (tag === 'glibc' || tag === 'musl') { |
|
tags.libc = tag |
|
} else { |
|
continue |
|
} |
|
|
|
tags.specificity++ |
|
} |
|
|
|
return tags |
|
} |
|
|
|
function matchTags (runtime, abi) { |
|
return function (tags) { |
|
if (tags == null) return false |
|
if (tags.runtime && tags.runtime !== runtime && !runtimeAgnostic(tags)) return false |
|
if (tags.abi && tags.abi !== abi && !tags.napi) return false |
|
if (tags.uv && tags.uv !== uv) return false |
|
if (tags.armv && tags.armv !== armv) return false |
|
if (tags.libc && tags.libc !== libc) return false |
|
|
|
return true |
|
} |
|
} |
|
|
|
function runtimeAgnostic (tags) { |
|
return tags.runtime === 'node' && tags.napi |
|
} |
|
|
|
function compareTags (runtime) { |
|
// Precedence: non-agnostic runtime, abi over napi, then by specificity. |
|
return function (a, b) { |
|
if (a.runtime !== b.runtime) { |
|
return a.runtime === runtime ? -1 : 1 |
|
} else if (a.abi !== b.abi) { |
|
return a.abi ? -1 : 1 |
|
} else if (a.specificity !== b.specificity) { |
|
return a.specificity > b.specificity ? -1 : 1 |
|
} else { |
|
return 0 |
|
} |
|
} |
|
} |
|
|
|
function isNwjs () { |
|
return !!(process.versions && process.versions.nw) |
|
} |
|
|
|
function isElectron () { |
|
if (process.versions && process.versions.electron) return true |
|
if (process.env.ELECTRON_RUN_AS_NODE) return true |
|
return typeof window !== 'undefined' && window.process && window.process.type === 'renderer' |
|
} |
|
|
|
function isAlpine (platform) { |
|
return platform === 'linux' && fs.existsSync('/etc/alpine-release') |
|
} |
|
|
|
// Exposed for unit tests |
|
// TODO: move to lib |
|
load.parseTags = parseTags |
|
load.matchTags = matchTags |
|
load.compareTags = compareTags |
|
load.parseTuple = parseTuple |
|
load.matchTuple = matchTuple |
|
load.compareTuples = compareTuples
|
|
|