node-gyp.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /**
  2. * Module exports.
  3. */
  4. module.exports = exports = gyp
  5. /**
  6. * Module dependencies.
  7. */
  8. var fs = require('graceful-fs')
  9. , path = require('path')
  10. , nopt = require('nopt')
  11. , log = require('npmlog')
  12. , child_process = require('child_process')
  13. , EE = require('events').EventEmitter
  14. , inherits = require('util').inherits
  15. , commands = [
  16. // Module build commands
  17. 'build'
  18. , 'clean'
  19. , 'configure'
  20. , 'rebuild'
  21. // Development Header File management commands
  22. , 'install'
  23. , 'list'
  24. , 'remove'
  25. ]
  26. , aliases = {
  27. 'ls': 'list'
  28. , 'rm': 'remove'
  29. }
  30. // differentiate node-gyp's logs from npm's
  31. log.heading = 'gyp'
  32. /**
  33. * The `gyp` function.
  34. */
  35. function gyp () {
  36. return new Gyp()
  37. }
  38. function Gyp () {
  39. var self = this
  40. this.devDir = ''
  41. this.commands = {}
  42. commands.forEach(function (command) {
  43. self.commands[command] = function (argv, callback) {
  44. log.verbose('command', command, argv)
  45. return require('./' + command)(self, argv, callback)
  46. }
  47. })
  48. }
  49. inherits(Gyp, EE)
  50. exports.Gyp = Gyp
  51. var proto = Gyp.prototype
  52. /**
  53. * Export the contents of the package.json.
  54. */
  55. proto.package = require('../package')
  56. /**
  57. * nopt configuration definitions
  58. */
  59. proto.configDefs = {
  60. help: Boolean // everywhere
  61. , arch: String // 'configure'
  62. , cafile: String // 'install'
  63. , debug: Boolean // 'build'
  64. , directory: String // bin
  65. , make: String // 'build'
  66. , msvs_version: String // 'configure'
  67. , ensure: Boolean // 'install'
  68. , solution: String // 'build' (windows only)
  69. , proxy: String // 'install'
  70. , devdir: String // everywhere
  71. , nodedir: String // 'configure'
  72. , loglevel: String // everywhere
  73. , python: String // 'configure'
  74. , 'dist-url': String // 'install'
  75. , 'tarball': String // 'install'
  76. , jobs: String // 'build'
  77. , thin: String // 'configure'
  78. }
  79. /**
  80. * nopt shorthands
  81. */
  82. proto.shorthands = {
  83. release: '--no-debug'
  84. , C: '--directory'
  85. , debug: '--debug'
  86. , j: '--jobs'
  87. , silly: '--loglevel=silly'
  88. , verbose: '--loglevel=verbose'
  89. , silent: '--loglevel=silent'
  90. }
  91. /**
  92. * expose the command aliases for the bin file to use.
  93. */
  94. proto.aliases = aliases
  95. /**
  96. * Parses the given argv array and sets the 'opts',
  97. * 'argv' and 'command' properties.
  98. */
  99. proto.parseArgv = function parseOpts (argv) {
  100. this.opts = nopt(this.configDefs, this.shorthands, argv)
  101. this.argv = this.opts.argv.remain.slice()
  102. var commands = this.todo = []
  103. // create a copy of the argv array with aliases mapped
  104. argv = this.argv.map(function (arg) {
  105. // is this an alias?
  106. if (arg in this.aliases) {
  107. arg = this.aliases[arg]
  108. }
  109. return arg
  110. }, this)
  111. // process the mapped args into "command" objects ("name" and "args" props)
  112. argv.slice().forEach(function (arg) {
  113. if (arg in this.commands) {
  114. var args = argv.splice(0, argv.indexOf(arg))
  115. argv.shift()
  116. if (commands.length > 0) {
  117. commands[commands.length - 1].args = args
  118. }
  119. commands.push({ name: arg, args: [] })
  120. }
  121. }, this)
  122. if (commands.length > 0) {
  123. commands[commands.length - 1].args = argv.splice(0)
  124. }
  125. // support for inheriting config env variables from npm
  126. var npm_config_prefix = 'npm_config_'
  127. Object.keys(process.env).forEach(function (name) {
  128. if (name.indexOf(npm_config_prefix) !== 0) return
  129. var val = process.env[name]
  130. if (name === npm_config_prefix + 'loglevel') {
  131. log.level = val
  132. } else {
  133. // add the user-defined options to the config
  134. name = name.substring(npm_config_prefix.length)
  135. // gyp@741b7f1 enters an infinite loop when it encounters
  136. // zero-length options so ensure those don't get through.
  137. if (name) this.opts[name] = val
  138. }
  139. }, this)
  140. if (this.opts.loglevel) {
  141. log.level = this.opts.loglevel
  142. }
  143. log.resume()
  144. }
  145. /**
  146. * Spawns a child process and emits a 'spawn' event.
  147. */
  148. proto.spawn = function spawn (command, args, opts) {
  149. if (!opts) opts = {}
  150. if (!opts.silent && !opts.stdio) {
  151. opts.stdio = [ 0, 1, 2 ]
  152. }
  153. var cp = child_process.spawn(command, args, opts)
  154. log.info('spawn', command)
  155. log.info('spawn args', args)
  156. return cp
  157. }
  158. /**
  159. * Returns the usage instructions for node-gyp.
  160. */
  161. proto.usage = function usage () {
  162. var str = [
  163. ''
  164. , ' Usage: node-gyp <command> [options]'
  165. , ''
  166. , ' where <command> is one of:'
  167. , commands.map(function (c) {
  168. return ' - ' + c + ' - ' + require('./' + c).usage
  169. }).join('\n')
  170. , ''
  171. , 'node-gyp@' + this.version + ' ' + path.resolve(__dirname, '..')
  172. , 'node@' + process.versions.node
  173. ].join('\n')
  174. return str
  175. }
  176. /**
  177. * Version number getter.
  178. */
  179. Object.defineProperty(proto, 'version', {
  180. get: function () {
  181. return this.package.version
  182. }
  183. , enumerable: true
  184. })