build.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. 'use strict'
  2. const fs = require('graceful-fs')
  3. const path = require('path')
  4. const glob = require('glob')
  5. const log = require('npmlog')
  6. const which = require('which')
  7. const win = process.platform === 'win32'
  8. function build (gyp, argv, callback) {
  9. var platformMake = 'make'
  10. if (process.platform === 'aix') {
  11. platformMake = 'gmake'
  12. } else if (process.platform.indexOf('bsd') !== -1) {
  13. platformMake = 'gmake'
  14. } else if (win && argv.length > 0) {
  15. argv = argv.map(function (target) {
  16. return '/t:' + target
  17. })
  18. }
  19. var makeCommand = gyp.opts.make || process.env.MAKE || platformMake
  20. var command = win ? 'msbuild' : makeCommand
  21. var jobs = gyp.opts.jobs || process.env.JOBS
  22. var buildType
  23. var config
  24. var arch
  25. var nodeDir
  26. var guessedSolution
  27. loadConfigGypi()
  28. /**
  29. * Load the "config.gypi" file that was generated during "configure".
  30. */
  31. function loadConfigGypi () {
  32. var configPath = path.resolve('build', 'config.gypi')
  33. fs.readFile(configPath, 'utf8', function (err, data) {
  34. if (err) {
  35. if (err.code === 'ENOENT') {
  36. callback(new Error('You must run `node-gyp configure` first!'))
  37. } else {
  38. callback(err)
  39. }
  40. return
  41. }
  42. config = JSON.parse(data.replace(/#.+\n/, ''))
  43. // get the 'arch', 'buildType', and 'nodeDir' vars from the config
  44. buildType = config.target_defaults.default_configuration
  45. arch = config.variables.target_arch
  46. nodeDir = config.variables.nodedir
  47. if ('debug' in gyp.opts) {
  48. buildType = gyp.opts.debug ? 'Debug' : 'Release'
  49. }
  50. if (!buildType) {
  51. buildType = 'Release'
  52. }
  53. log.verbose('build type', buildType)
  54. log.verbose('architecture', arch)
  55. log.verbose('node dev dir', nodeDir)
  56. if (win) {
  57. findSolutionFile()
  58. } else {
  59. doWhich()
  60. }
  61. })
  62. }
  63. /**
  64. * On Windows, find the first build/*.sln file.
  65. */
  66. function findSolutionFile () {
  67. glob('build/*.sln', function (err, files) {
  68. if (err) {
  69. return callback(err)
  70. }
  71. if (files.length === 0) {
  72. return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
  73. }
  74. guessedSolution = files[0]
  75. log.verbose('found first Solution file', guessedSolution)
  76. doWhich()
  77. })
  78. }
  79. /**
  80. * Uses node-which to locate the msbuild / make executable.
  81. */
  82. function doWhich () {
  83. // On Windows use msbuild provided by node-gyp configure
  84. if (win) {
  85. if (!config.variables.msbuild_path) {
  86. return callback(new Error(
  87. 'MSBuild is not set, please run `node-gyp configure`.'))
  88. }
  89. command = config.variables.msbuild_path
  90. log.verbose('using MSBuild:', command)
  91. doBuild()
  92. return
  93. }
  94. // First make sure we have the build command in the PATH
  95. which(command, function (err, execPath) {
  96. if (err) {
  97. // Some other error or 'make' not found on Unix, report that to the user
  98. callback(err)
  99. return
  100. }
  101. log.verbose('`which` succeeded for `' + command + '`', execPath)
  102. doBuild()
  103. })
  104. }
  105. /**
  106. * Actually spawn the process and compile the module.
  107. */
  108. function doBuild () {
  109. // Enable Verbose build
  110. var verbose = log.levels[log.level] <= log.levels.verbose
  111. var j
  112. if (!win && verbose) {
  113. argv.push('V=1')
  114. }
  115. if (win && !verbose) {
  116. argv.push('/clp:Verbosity=minimal')
  117. }
  118. if (win) {
  119. // Turn off the Microsoft logo on Windows
  120. argv.push('/nologo')
  121. }
  122. // Specify the build type, Release by default
  123. if (win) {
  124. // Convert .gypi config target_arch to MSBuild /Platform
  125. // Since there are many ways to state '32-bit Intel', default to it.
  126. // N.B. msbuild's Condition string equality tests are case-insensitive.
  127. var archLower = arch.toLowerCase()
  128. var p = archLower === 'x64' ? 'x64'
  129. : (archLower === 'arm' ? 'ARM'
  130. : (archLower === 'arm64' ? 'ARM64' : 'Win32'))
  131. argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
  132. if (jobs) {
  133. j = parseInt(jobs, 10)
  134. if (!isNaN(j) && j > 0) {
  135. argv.push('/m:' + j)
  136. } else if (jobs.toUpperCase() === 'MAX') {
  137. argv.push('/m:' + require('os').cpus().length)
  138. }
  139. }
  140. } else {
  141. argv.push('BUILDTYPE=' + buildType)
  142. // Invoke the Makefile in the 'build' dir.
  143. argv.push('-C')
  144. argv.push('build')
  145. if (jobs) {
  146. j = parseInt(jobs, 10)
  147. if (!isNaN(j) && j > 0) {
  148. argv.push('--jobs')
  149. argv.push(j)
  150. } else if (jobs.toUpperCase() === 'MAX') {
  151. argv.push('--jobs')
  152. argv.push(require('os').cpus().length)
  153. }
  154. }
  155. }
  156. if (win) {
  157. // did the user specify their own .sln file?
  158. var hasSln = argv.some(function (arg) {
  159. return path.extname(arg) === '.sln'
  160. })
  161. if (!hasSln) {
  162. argv.unshift(gyp.opts.solution || guessedSolution)
  163. }
  164. }
  165. var proc = gyp.spawn(command, argv)
  166. proc.on('exit', onExit)
  167. }
  168. function onExit (code, signal) {
  169. if (code !== 0) {
  170. return callback(new Error('`' + command + '` failed with exit code: ' + code))
  171. }
  172. if (signal) {
  173. return callback(new Error('`' + command + '` got signal: ' + signal))
  174. }
  175. callback()
  176. }
  177. }
  178. module.exports = build
  179. module.exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'