node-gyp-build.js 5.9 KB

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