Skip to content

feat: promisify build #2358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 81 additions & 96 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,130 +2,114 @@

const fs = require('graceful-fs')
const path = require('path')
const util = require('util')
const glob = require('glob')
const log = require('npmlog')
const which = require('which')
const win = process.platform === 'win32'

function build (gyp, argv, callback) {
var platformMake = 'make'
async function build (gyp, argv) {
let platformMake = 'make'
if (process.platform === 'aix') {
platformMake = 'gmake'
} else if (process.platform.indexOf('bsd') !== -1) {
platformMake = 'gmake'
} else if (win && argv.length > 0) {
argv = argv.map(function (target) {
return '/t:' + target
})
argv = argv.map(target => '/t:' + target)
}

var makeCommand = gyp.opts.make || process.env.MAKE || platformMake
var command = win ? 'msbuild' : makeCommand
var jobs = gyp.opts.jobs || process.env.JOBS
var buildType
var config
var arch
var nodeDir
var guessedSolution

loadConfigGypi()
const makeCommand = gyp.opts.make || process.env.MAKE || platformMake
const jobs = gyp.opts.jobs || process.env.JOBS
let command = win ? 'msbuild' : makeCommand
let buildType
let config
let arch
let nodeDir
let guessedSolution

await loadConfigGypi()
if (win) {
await findSolutionFile()
}
await doWhich()
await doBuild()

/**
* Load the "config.gypi" file that was generated during "configure".
*/

function loadConfigGypi () {
var configPath = path.resolve('build', 'config.gypi')
async function loadConfigGypi () {
const configPath = path.resolve('build', 'config.gypi')

fs.readFile(configPath, 'utf8', function (err, data) {
if (err) {
if (err.code === 'ENOENT') {
callback(new Error('You must run `node-gyp configure` first!'))
} else {
callback(err)
}
return
}
config = JSON.parse(data.replace(/#.+\n/, ''))

// get the 'arch', 'buildType', and 'nodeDir' vars from the config
buildType = config.target_defaults.default_configuration
arch = config.variables.target_arch
nodeDir = config.variables.nodedir
try {
const data = await fs.promises.readFile(configPath, 'utf8')

if ('debug' in gyp.opts) {
buildType = gyp.opts.debug ? 'Debug' : 'Release'
}
if (!buildType) {
buildType = 'Release'
config = JSON.parse(data.replace(/#.+\n/, ''))
} catch (err) {
if (err.code === 'ENOENT') {
throw new Error('You must run `node-gyp configure` first!')
}
throw err
}

log.verbose('build type', buildType)
log.verbose('architecture', arch)
log.verbose('node dev dir', nodeDir)
// get the 'arch', 'buildType', and 'nodeDir' vars from the config
buildType = config.target_defaults.default_configuration
arch = config.variables.target_arch
nodeDir = config.variables.nodedir

if (win) {
findSolutionFile()
} else {
doWhich()
}
})
if ('debug' in gyp.opts) {
buildType = gyp.opts.debug ? 'Debug' : 'Release'
}
if (!buildType) {
buildType = 'Release'
}

log.verbose('build type', buildType)
log.verbose('architecture', arch)
log.verbose('node dev dir', nodeDir)
}

/**
* On Windows, find the first build/*.sln file.
*/

function findSolutionFile () {
glob('build/*.sln', function (err, files) {
if (err) {
return callback(err)
}
if (files.length === 0) {
return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
}
guessedSolution = files[0]
log.verbose('found first Solution file', guessedSolution)
doWhich()
})
async function findSolutionFile () {
const files = await util.promisify(glob)('build/*.sln')
if (files.length === 0) {
throw new Error('Could not find *.sln file. Did you run "configure"?')
}
guessedSolution = files[0]
log.verbose('found first Solution file', guessedSolution)
}

/**
* Uses node-which to locate the msbuild / make executable.
*/

function doWhich () {
async function doWhich () {
// On Windows use msbuild provided by node-gyp configure
if (win) {
if (!config.variables.msbuild_path) {
return callback(new Error(
'MSBuild is not set, please run `node-gyp configure`.'))
throw new Error('MSBuild is not set, please run `node-gyp configure`.')
}
command = config.variables.msbuild_path
log.verbose('using MSBuild:', command)
doBuild()
return
}
// First make sure we have the build command in the PATH
which(command, function (err, execPath) {
if (err) {
// Some other error or 'make' not found on Unix, report that to the user
callback(err)
return
}
} else {
// First make sure we have the build command in the PATH
// If there's an error or 'make' not found on Unix, propagate that to the user
const execPath = await which(command)
log.verbose('`which` succeeded for `' + command + '`', execPath)
doBuild()
})
}
}

/**
* Actually spawn the process and compile the module.
*/

function doBuild () {
async function doBuild () {
// Enable Verbose build
var verbose = log.levels[log.level] <= log.levels.verbose
var j
const verbose = log.levels[log.level] <= log.levels.verbose
let j

if (!win && verbose) {
argv.push('V=1')
Expand All @@ -145,11 +129,11 @@ function build (gyp, argv, callback) {
// Convert .gypi config target_arch to MSBuild /Platform
// Since there are many ways to state '32-bit Intel', default to it.
// N.B. msbuild's Condition string equality tests are case-insensitive.
var archLower = arch.toLowerCase()
var p = archLower === 'x64' ? 'x64'
const archLower = arch.toLowerCase()
const p = archLower === 'x64' ? 'x64'
: (archLower === 'arm' ? 'ARM'
: (archLower === 'arm64' ? 'ARM64' : 'Win32'))
argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
argv.push(`/p:Configuration=${buildType};Platform=${p}`)
if (jobs) {
j = parseInt(jobs, 10)
if (!isNaN(j) && j > 0) {
Expand Down Expand Up @@ -177,28 +161,29 @@ function build (gyp, argv, callback) {

if (win) {
// did the user specify their own .sln file?
var hasSln = argv.some(function (arg) {
return path.extname(arg) === '.sln'
})
const hasSln = argv.some(arg => path.extname(arg) === '.sln')
if (!hasSln) {
argv.unshift(gyp.opts.solution || guessedSolution)
}
}

var proc = gyp.spawn(command, argv)
proc.on('exit', onExit)
}

function onExit (code, signal) {
if (code !== 0) {
return callback(new Error('`' + command + '` failed with exit code: ' + code))
}
if (signal) {
return callback(new Error('`' + command + '` got signal: ' + signal))
}
callback()
return new Promise((resolve, reject) => {
const proc = gyp.spawn(command, argv)
proc.on('exit', (code, signal) => {
if (code !== 0) {
return reject(new Error('`' + command + '` failed with exit code: ' + code))
}
if (signal) {
return reject(new Error('`' + command + '` got signal: ' + signal))
}
resolve()
})
})
}
}

module.exports = build
module.exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
module.exports = function (gyp, argv, callback) {
// expects no fullfiled response.
build(gyp, argv).then(() => callback(), callback)
}
module.exports.usage = `Invokes \`${win ? 'msbuild' : 'make'}\` and builds the module`