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