Initial commit
[yaffs-website] / node_modules / node-gyp / lib / build.js
1
2 module.exports = exports = build
3
4 /**
5  * Module dependencies.
6  */
7
8 var fs = require('graceful-fs')
9   , rm = require('rimraf')
10   , path = require('path')
11   , glob = require('glob')
12   , log = require('npmlog')
13   , which = require('which')
14   , mkdirp = require('mkdirp')
15   , exec = require('child_process').exec
16   , processRelease = require('./process-release')
17   , win = process.platform == 'win32'
18
19 exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
20
21 function build (gyp, argv, callback) {
22   var platformMake = 'make'
23   if (process.platform === 'aix') {
24     platformMake = 'gmake'
25   } else if (process.platform.indexOf('bsd') !== -1) {
26     platformMake = 'gmake'
27   }
28
29   var release = processRelease(argv, gyp, process.version, process.release)
30     , makeCommand = gyp.opts.make || process.env.MAKE || platformMake
31     , command = win ? 'msbuild' : makeCommand
32     , buildDir = path.resolve('build')
33     , configPath = path.resolve(buildDir, 'config.gypi')
34     , jobs = gyp.opts.jobs || process.env.JOBS
35     , buildType
36     , config
37     , arch
38     , nodeDir
39     , copyDevLib
40
41   loadConfigGypi()
42
43   /**
44    * Load the "config.gypi" file that was generated during "configure".
45    */
46
47   function loadConfigGypi () {
48     fs.readFile(configPath, 'utf8', function (err, data) {
49       if (err) {
50         if (err.code == 'ENOENT') {
51           callback(new Error('You must run `node-gyp configure` first!'))
52         } else {
53           callback(err)
54         }
55         return
56       }
57       config = JSON.parse(data.replace(/\#.+\n/, ''))
58
59       // get the 'arch', 'buildType', and 'nodeDir' vars from the config
60       buildType = config.target_defaults.default_configuration
61       arch = config.variables.target_arch
62       nodeDir = config.variables.nodedir
63       copyDevLib = config.variables.copy_dev_lib == 'true'
64
65       if ('debug' in gyp.opts) {
66         buildType = gyp.opts.debug ? 'Debug' : 'Release'
67       }
68       if (!buildType) {
69         buildType = 'Release'
70       }
71
72       log.verbose('build type', buildType)
73       log.verbose('architecture', arch)
74       log.verbose('node dev dir', nodeDir)
75
76       if (win) {
77         findSolutionFile()
78       } else {
79         doWhich()
80       }
81     })
82   }
83
84   /**
85    * On Windows, find the first build/*.sln file.
86    */
87
88   function findSolutionFile () {
89     glob('build/*.sln', function (err, files) {
90       if (err) return callback(err)
91       if (files.length === 0) {
92         return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
93       }
94       guessedSolution = files[0]
95       log.verbose('found first Solution file', guessedSolution)
96       doWhich()
97     })
98   }
99
100   /**
101    * Uses node-which to locate the msbuild / make executable.
102    */
103
104   function doWhich () {
105     // First make sure we have the build command in the PATH
106     which(command, function (err, execPath) {
107       if (err) {
108         if (win && /not found/.test(err.message)) {
109           // On windows and no 'msbuild' found. Let's guess where it is
110           findMsbuild()
111         } else {
112           // Some other error or 'make' not found on Unix, report that to the user
113           callback(err)
114         }
115         return
116       }
117       log.verbose('`which` succeeded for `' + command + '`', execPath)
118       copyNodeLib()
119     })
120   }
121
122   /**
123    * Search for the location of "msbuild.exe" file on Windows.
124    */
125
126   function findMsbuild () {
127     log.verbose('could not find "msbuild.exe" in PATH - finding location in registry')
128     var notfoundErr = 'Can\'t find "msbuild.exe". Do you have Microsoft Visual Studio C++ 2008+ installed?'
129     var cmd = 'reg query "HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions" /s'
130     if (process.arch !== 'ia32')
131       cmd += ' /reg:32'
132     exec(cmd, function (err, stdout, stderr) {
133       if (err) {
134         return callback(new Error(err.message + '\n' + notfoundErr))
135       }
136       var reVers = /ToolsVersions\\([^\\]+)$/i
137         , rePath = /\r\n[ \t]+MSBuildToolsPath[ \t]+REG_SZ[ \t]+([^\r]+)/i
138         , msbuilds = []
139         , r
140         , msbuildPath
141       stdout.split('\r\n\r\n').forEach(function(l) {
142         if (!l) return
143         l = l.trim()
144         if (r = reVers.exec(l.substring(0, l.indexOf('\r\n')))) {
145           var ver = parseFloat(r[1], 10)
146           if (ver >= 3.5) {
147             if (r = rePath.exec(l)) {
148               msbuilds.push({
149                 version: ver,
150                 path: r[1]
151               })
152             }
153           }
154         }
155       })
156       msbuilds.sort(function (x, y) {
157         return (x.version < y.version ? -1 : 1)
158       })
159       ;(function verifyMsbuild () {
160         if (!msbuilds.length) return callback(new Error(notfoundErr))
161         msbuildPath = path.resolve(msbuilds.pop().path, 'msbuild.exe')
162         fs.stat(msbuildPath, function (err, stat) {
163           if (err) {
164             if (err.code == 'ENOENT') {
165               if (msbuilds.length) {
166                 return verifyMsbuild()
167               } else {
168                 callback(new Error(notfoundErr))
169               }
170             } else {
171               callback(err)
172             }
173             return
174           }
175           command = msbuildPath
176           copyNodeLib()
177         })
178       })()
179     })
180   }
181
182   /**
183    * Copies the node.lib file for the current target architecture into the
184    * current proper dev dir location.
185    */
186
187   function copyNodeLib () {
188     if (!win || !copyDevLib) return doBuild()
189
190     var buildDir = path.resolve(nodeDir, buildType)
191       , archNodeLibPath = path.resolve(nodeDir, arch, release.name + '.lib')
192       , buildNodeLibPath = path.resolve(buildDir, release.name + '.lib')
193
194     mkdirp(buildDir, function (err, isNew) {
195       if (err) return callback(err)
196       log.verbose('"' + buildType + '" dir needed to be created?', isNew)
197       var rs = fs.createReadStream(archNodeLibPath)
198         , ws = fs.createWriteStream(buildNodeLibPath)
199       log.verbose('copying "' + release.name + '.lib" for ' + arch, buildNodeLibPath)
200       rs.pipe(ws)
201       rs.on('error', callback)
202       ws.on('error', callback)
203       rs.on('end', doBuild)
204     })
205   }
206
207   /**
208    * Actually spawn the process and compile the module.
209    */
210
211   function doBuild () {
212
213     // Enable Verbose build
214     var verbose = log.levels[log.level] <= log.levels.verbose
215     if (!win && verbose) {
216       argv.push('V=1')
217     }
218     if (win && !verbose) {
219       argv.push('/clp:Verbosity=minimal')
220     }
221
222     if (win) {
223       // Turn off the Microsoft logo on Windows
224       argv.push('/nologo')
225     }
226
227     // Specify the build type, Release by default
228     if (win) {
229       var p = arch === 'x64' ? 'x64' : 'Win32'
230       argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
231       if (jobs) {
232         var j = parseInt(jobs, 10)
233         if (!isNaN(j) && j > 0) {
234           argv.push('/m:' + j)
235         } else if (jobs.toUpperCase() === 'MAX') {
236           argv.push('/m:' + require('os').cpus().length)
237         }
238       }
239     } else {
240       argv.push('BUILDTYPE=' + buildType)
241       // Invoke the Makefile in the 'build' dir.
242       argv.push('-C')
243       argv.push('build')
244       if (jobs) {
245         var j = parseInt(jobs, 10)
246         if (!isNaN(j) && j > 0) {
247           argv.push('--jobs')
248           argv.push(j)
249         } else if (jobs.toUpperCase() === 'MAX') {
250           argv.push('--jobs')
251           argv.push(require('os').cpus().length)
252         }
253       }
254     }
255
256     if (win) {
257       // did the user specify their own .sln file?
258       var hasSln = argv.some(function (arg) {
259         return path.extname(arg) == '.sln'
260       })
261       if (!hasSln) {
262         argv.unshift(gyp.opts.solution || guessedSolution)
263       }
264     }
265
266     var proc = gyp.spawn(command, argv)
267     proc.on('exit', onExit)
268   }
269
270   /**
271    * Invoked after the make/msbuild command exits.
272    */
273
274   function onExit (code, signal) {
275     if (code !== 0) {
276       return callback(new Error('`' + command + '` failed with exit code: ' + code))
277     }
278     if (signal) {
279       return callback(new Error('`' + command + '` got signal: ' + signal))
280     }
281     callback()
282   }
283
284 }