|
| 1 | +var fs = require('fs') |
| 2 | +var path = require('path') |
| 3 | +var os = require('os') |
| 4 | + |
| 5 | +// Workaround to fix webpack's build warnings: 'the request of a dependency is an expression' |
| 6 | +var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line |
| 7 | + |
| 8 | +var vars = (process.config && process.config.variables) || {} |
| 9 | +var prebuildsOnly = !!process.env.PREBUILDS_ONLY |
| 10 | +var abi = process.versions.modules // TODO: support old node where this is undef |
| 11 | +var runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node') |
| 12 | + |
| 13 | +var arch = process.env.npm_config_arch || os.arch() |
| 14 | +var platform = process.env.npm_config_platform || os.platform() |
| 15 | +var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') |
| 16 | +// ARMv7 detection patched to avoid arm_version === "default" on other arm systems than arm64 ones |
| 17 | +var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : (arch === 'arm' ? (vars.arm_version === 'default' ? '7' : vars.arm_version) : '')) || '' |
| 18 | +var uv = (process.versions.uv || '').split('.')[0] |
| 19 | + |
| 20 | +module.exports = load |
| 21 | + |
| 22 | +function load (dir) { |
| 23 | + return runtimeRequire(load.path(dir)) |
| 24 | +} |
| 25 | + |
| 26 | +load.path = function (dir) { |
| 27 | + dir = path.resolve(dir || '.') |
| 28 | + |
| 29 | + try { |
| 30 | + var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_') |
| 31 | + if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD'] |
| 32 | + } catch (err) {} |
| 33 | + |
| 34 | + if (!prebuildsOnly) { |
| 35 | + var release = getFirst(path.join(dir, 'build/Release'), matchBuild) |
| 36 | + if (release) return release |
| 37 | + |
| 38 | + var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild) |
| 39 | + if (debug) return debug |
| 40 | + } |
| 41 | + |
| 42 | + var prebuild = resolve(dir) |
| 43 | + if (prebuild) return prebuild |
| 44 | + |
| 45 | + var nearby = resolve(path.dirname(process.execPath)) |
| 46 | + if (nearby) return nearby |
| 47 | + |
| 48 | + var target = [ |
| 49 | + 'platform=' + platform, |
| 50 | + 'arch=' + arch, |
| 51 | + 'runtime=' + runtime, |
| 52 | + 'abi=' + abi, |
| 53 | + 'uv=' + uv, |
| 54 | + armv ? 'armv=' + armv : '', |
| 55 | + 'libc=' + libc, |
| 56 | + 'node=' + process.versions.node, |
| 57 | + process.versions.electron ? 'electron=' + process.versions.electron : '', |
| 58 | + typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line |
| 59 | + ].filter(Boolean).join(' ') |
| 60 | + |
| 61 | + throw new Error('No native build was found for ' + target + '\n loaded from: ' + dir + '\n') |
| 62 | + |
| 63 | + function resolve (dir) { |
| 64 | + // Find matching "prebuilds/<platform>-<arch>" directory |
| 65 | + var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple) |
| 66 | + var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0] |
| 67 | + if (!tuple) return |
| 68 | + |
| 69 | + // Find most specific flavor first |
| 70 | + var prebuilds = path.join(dir, 'prebuilds', tuple.name) |
| 71 | + var parsed = readdirSync(prebuilds).map(parseTags) |
| 72 | + var candidates = parsed.filter(matchTags(runtime, abi)) |
| 73 | + var winner = candidates.sort(compareTags(runtime))[0] |
| 74 | + if (winner) return path.join(prebuilds, winner.file) |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +function readdirSync (dir) { |
| 79 | + try { |
| 80 | + return fs.readdirSync(dir) |
| 81 | + } catch (err) { |
| 82 | + return [] |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +function getFirst (dir, filter) { |
| 87 | + var files = readdirSync(dir).filter(filter) |
| 88 | + return files[0] && path.join(dir, files[0]) |
| 89 | +} |
| 90 | + |
| 91 | +function matchBuild (name) { |
| 92 | + return /\.node$/.test(name) |
| 93 | +} |
| 94 | + |
| 95 | +function parseTuple (name) { |
| 96 | + // Example: darwin-x64+arm64 |
| 97 | + var arr = name.split('-') |
| 98 | + if (arr.length !== 2) return |
| 99 | + |
| 100 | + var platform = arr[0] |
| 101 | + var architectures = arr[1].split('+') |
| 102 | + |
| 103 | + if (!platform) return |
| 104 | + if (!architectures.length) return |
| 105 | + if (!architectures.every(Boolean)) return |
| 106 | + |
| 107 | + return { name, platform, architectures } |
| 108 | +} |
| 109 | + |
| 110 | +function matchTuple (platform, arch) { |
| 111 | + return function (tuple) { |
| 112 | + if (tuple == null) return false |
| 113 | + if (tuple.platform !== platform) return false |
| 114 | + return tuple.architectures.includes(arch) |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +function compareTuples (a, b) { |
| 119 | + // Prefer single-arch prebuilds over multi-arch |
| 120 | + return a.architectures.length - b.architectures.length |
| 121 | +} |
| 122 | + |
| 123 | +function parseTags (file) { |
| 124 | + var arr = file.split('.') |
| 125 | + var extension = arr.pop() |
| 126 | + var tags = { file: file, specificity: 0 } |
| 127 | + |
| 128 | + if (extension !== 'node') return |
| 129 | + |
| 130 | + for (var i = 0; i < arr.length; i++) { |
| 131 | + var tag = arr[i] |
| 132 | + |
| 133 | + if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') { |
| 134 | + tags.runtime = tag |
| 135 | + } else if (tag === 'napi') { |
| 136 | + tags.napi = true |
| 137 | + } else if (tag.slice(0, 3) === 'abi') { |
| 138 | + tags.abi = tag.slice(3) |
| 139 | + } else if (tag.slice(0, 2) === 'uv') { |
| 140 | + tags.uv = tag.slice(2) |
| 141 | + } else if (tag.slice(0, 4) === 'armv') { |
| 142 | + tags.armv = tag.slice(4) |
| 143 | + } else if (tag === 'glibc' || tag === 'musl') { |
| 144 | + tags.libc = tag |
| 145 | + } else { |
| 146 | + continue |
| 147 | + } |
| 148 | + |
| 149 | + tags.specificity++ |
| 150 | + } |
| 151 | + |
| 152 | + return tags |
| 153 | +} |
| 154 | + |
| 155 | +function matchTags (runtime, abi) { |
| 156 | + return function (tags) { |
| 157 | + if (tags == null) return false |
| 158 | + if (tags.runtime !== runtime && !runtimeAgnostic(tags)) return false |
| 159 | + if (tags.abi !== abi && !tags.napi) return false |
| 160 | + if (tags.uv && tags.uv !== uv) return false |
| 161 | + if (tags.armv && tags.armv !== armv) return false |
| 162 | + if (tags.libc && tags.libc !== libc) return false |
| 163 | + |
| 164 | + return true |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | +function runtimeAgnostic (tags) { |
| 169 | + return tags.runtime === 'node' && tags.napi |
| 170 | +} |
| 171 | + |
| 172 | +function compareTags (runtime) { |
| 173 | + // Precedence: non-agnostic runtime, abi over napi, then by specificity. |
| 174 | + return function (a, b) { |
| 175 | + if (a.runtime !== b.runtime) { |
| 176 | + return a.runtime === runtime ? -1 : 1 |
| 177 | + } else if (a.abi !== b.abi) { |
| 178 | + return a.abi ? -1 : 1 |
| 179 | + } else if (a.specificity !== b.specificity) { |
| 180 | + return a.specificity > b.specificity ? -1 : 1 |
| 181 | + } else { |
| 182 | + return 0 |
| 183 | + } |
| 184 | + } |
| 185 | +} |
| 186 | + |
| 187 | +function isNwjs () { |
| 188 | + return !!(process.versions && process.versions.nw) |
| 189 | +} |
| 190 | + |
| 191 | +function isElectron () { |
| 192 | + if (process.versions && process.versions.electron) return true |
| 193 | + if (process.env.ELECTRON_RUN_AS_NODE) return true |
| 194 | + return typeof window !== 'undefined' && window.process && window.process.type === 'renderer' |
| 195 | +} |
| 196 | + |
| 197 | +function isAlpine (platform) { |
| 198 | + return platform === 'linux' && fs.existsSync('/etc/alpine-release') |
| 199 | +} |
| 200 | + |
| 201 | +// Exposed for unit tests |
| 202 | +// TODO: move to lib |
| 203 | +load.parseTags = parseTags |
| 204 | +load.matchTags = matchTags |
| 205 | +load.compareTags = compareTags |
| 206 | +load.parseTuple = parseTuple |
| 207 | +load.matchTuple = matchTuple |
| 208 | +load.compareTuples = compareTuples |
0 commit comments