Skip to content
Closed
Show file tree
Hide file tree
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
216 changes: 131 additions & 85 deletions bin/node-gyp.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,158 @@
process.title = 'node-gyp'

const envPaths = require('env-paths')
const gyp = require('../')
const Gyp = require('../')
const log = require('npmlog')
const os = require('os')
const fs = require('fs').promises

/**
* Process and execute the selected commands.
*/

const prog = gyp()
var completed = false
prog.parseArgv(process.argv)
prog.devDir = prog.opts.devdir

var homeDir = os.homedir()
if (prog.devDir) {
prog.devDir = prog.devDir.replace(/^~/, homeDir)
} else if (homeDir) {
prog.devDir = envPaths('node-gyp', { suffix: '' }).cache
} else {
throw new Error(
"node-gyp requires that the user's home directory is specified " +
'in either of the environmental variables HOME or USERPROFILE. ' +
'Overide with: --devdir /path/to/.node-gyp')
let completed = false

async function run () {
const gyp = new Gyp()
gyp.parseArgv(process.argv)

printVersion(gyp)
setupDevDir(gyp)

log.info('it worked if it ends with', 'ok')
log.verbose('cli', process.argv)
log.info('using', `node-gyp@${gyp.version}`)
log.info('using', `node@${process.versions.node} | ${process.platform} | ${process.arch}`)

await chdir(gyp)
await execute(gyp)
}

if (prog.todo.length === 0) {
if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) {
console.log('v%s', prog.version)
} else {
console.log('%s', prog.usage())
function printVersion (gyp) {
if (gyp.todo.length === 0) {
if (process.argv.includes('-v') || process.argv.includes('--version')) {
console.log('v%s', gyp.version)
} else {
console.log('%s', gyp.usage())
}
process.exit(0)
}
process.exit(0)
}

log.info('it worked if it ends with', 'ok')
log.verbose('cli', process.argv)
log.info('using', 'node-gyp@%s', prog.version)
log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch)
function setupDevDir (gyp) {
gyp.devDir = gyp.opts.devdir

const homeDir = os.homedir()
if (gyp.devDir) {
gyp.devDir = gyp.devDir.replace(/^~/, homeDir)
} else if (homeDir) {
gyp.devDir = envPaths('node-gyp', { suffix: '' }).cache
} else {
throw new Error(
'node-gyp requires that the user\'s home directory is specified ' +
'in either of the environmental variables HOME or USERPROFILE. ' +
'Overide with: --devdir /path/to/.node-gyp')
}
}

/**
* Change dir if -C/--directory was passed.
*/

var dir = prog.opts.directory
if (dir) {
var fs = require('fs')
try {
var stat = fs.statSync(dir)
if (stat.isDirectory()) {
log.info('chdir', dir)
process.chdir(dir)
} else {
log.warn('chdir', dir + ' is not a directory')
}
} catch (e) {
if (e.code === 'ENOENT') {
log.warn('chdir', dir + ' is not a directory')
} else {
log.warn('chdir', 'error during chdir() "%s"', e.message)
async function chdir (gyp) {
const dir = gyp.opts.directory
if (dir) {
try {
const stat = await fs.stat(dir)
if (stat.isDirectory()) {
log.info('chdir', dir)
process.chdir(dir)
} else {
log.warn('chdir', dir + ' is not a directory')
}
} catch (e) {
if (e.code === 'ENOENT') {
log.warn('chdir', dir + ' is not a directory')
} else {
log.warn('chdir', 'error during chdir() "%s"', e.message)
}
}
}
}

function run () {
var command = prog.todo.shift()
if (!command) {
// done!
completed = true
log.info('ok')
return
}

prog.commands[command.name](command.args, function (err) {
if (err) {
log.error(command.name + ' error')
log.error('stack', err.stack)
errorMessage()
log.error('not ok')
return process.exit(1)
const asyncCommands = [
'clean'
]

async function execute (gyp) {
while (true) {
const command = gyp.todo.shift()
if (!command) {
// done!
completed = true
log.info('ok')
return
}
if (command.name === 'list') {
var versions = arguments[1]
if (versions.length > 0) {
versions.forEach(function (version) {
console.log(version)
})
} else {
console.log('No node development files installed. Use `node-gyp install` to install a version.')

if (asyncCommands.includes(command.name)) {
try {
const result = await gyp.commands[command.name](command.args)

if (command.name === 'list') {
const versions = result[0]
if (versions.length > 0) {
versions.forEach((version) => console.log(version))
} else {
console.log('No node development files installed. Use `node-gyp install` to install a version.')
}
} else if (Array.isArray(result) && result.length) {
console.log.apply(console, result)
}
} catch (err) {
log.error(command.name + ' error')
log.error('stack', err.stack)
errorMessage()
log.error('not ok')
return process.exit(1)
}
} else if (arguments.length >= 2) {
console.log.apply(console, [].slice.call(arguments, 1))
} else {
// TODO: removeme
await new Promise((resolve, reject) => {
gyp.commands[command.name](command.args, (err, ...args) => {
if (err) {
log.error(command.name + ' error')
log.error('stack', err.stack)
errorMessage()
log.error('not ok')
return process.exit(1)
}

if (command.name === 'list') {
const versions = args[0]
if (versions.length > 0) {
versions.forEach((version) => console.log(version))
} else {
console.log('No node development files installed. Use `node-gyp install` to install a version.')
}
} else if (args.length >= 1) {
console.log.apply(console, args)
}

resolve()
})
})
}

// now run the next command in the queue
process.nextTick(run)
})
}
}

process.on('exit', function (code) {
process.on('exit', (code) => {
if (!completed && !code) {
log.error('Completion callback never invoked!')
issueMessage()
process.exit(6)
}
})

process.on('uncaughtException', function (err) {
process.on('uncaughtException', (err) => {
log.error('UNCAUGHT EXCEPTION')
log.error('stack', err.stack)
issueMessage()
Expand All @@ -120,21 +165,22 @@ process.on('uncaughtException', function (err) {

function errorMessage () {
// copied from npm's lib/utils/error-handler.js
var os = require('os')
log.error('System', os.type() + ' ' + os.release())
log.error('command', process.argv
.map(JSON.stringify).join(' '))
const os = require('os')
log.error('System', `${os.type()}${os.release()}`)
log.error('command', process.argv.map(JSON.stringify).join(' '))
log.error('cwd', process.cwd())
log.error('node -v', process.version)
log.error('node-gyp -v', 'v' + prog.package.version)
log.error('node-gyp -v', `v${require('../package.json').version}`)
}

function issueMessage () {
errorMessage()
log.error('', ['Node-gyp failed to build your package.',
'Try to update npm and/or node-gyp and if it does not help file an issue with the package author.'
].join('\n'))
log.error('',
`node-gyp failed to build your package.
Try to update npm and/or node-gyp and if it does not help file an issue with the package author.`)
}

// start running the given commands!
run()
run().catch((err) => {
console.error(err.stack)
process.exit(1)
})
120 changes: 120 additions & 0 deletions lib/args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const nopt = require('nopt')
const log = require('npmlog')

const commands = [
// Module build commands
'build',
'clean',
'configure',
'rebuild',
// Development Header File management commands
'install',
'list',
'remove'
]

const aliases = {
ls: 'list',
rm: 'remove'
}

const shorthands = {
release: '--no-debug',
C: '--directory',
debug: '--debug',
j: '--jobs',
silly: '--loglevel=silly',
verbose: '--loglevel=verbose',
silent: '--loglevel=silent'
}

const configDefs = {
help: Boolean, // everywhere
arch: String, // 'configure'
cafile: String, // 'install'
debug: Boolean, // 'build'
directory: String, // bin
make: String, // 'build'
msvs_version: String, // 'configure'
ensure: Boolean, // 'install'
solution: String, // 'build' (windows only)
proxy: String, // 'install'
noproxy: String, // 'install'
devdir: String, // everywhere
nodedir: String, // 'configure'
loglevel: String, // everywhere
python: String, // 'configure'
'dist-url': String, // 'install'
tarball: String, // 'install'
jobs: String, // 'build'
thin: String // 'configure'
}

/**
* Parses the given argv array and sets the 'opts',
* 'argv' and 'command' properties.
*/
function parseArgv (argv) {
const opts = nopt(configDefs, shorthands, argv)
argv = opts.argv.remain.slice()

const todo = []

// create a copy of the argv array with aliases mapped
argv = argv.map((arg) => {
// is this an alias?
if (arg in aliases) {
arg = aliases[arg]
}
return arg
})

// process the mapped args into "command" objects ("name" and "args" props)
argv.slice().forEach((arg) => {
if (commands.includes(arg)) {
const args = argv.splice(0, argv.indexOf(arg))
argv.shift()
if (todo.length > 0) {
todo[todo.length - 1].args = args
}
todo.push({ name: arg, args: [] })
}
})
if (todo.length > 0) {
todo[todo.length - 1].args = argv.splice(0)
}

// support for inheriting config env variables from npm
const npmConfigPrefix = 'npm_config_'
Object.keys(process.env).forEach((name) => {
if (name.indexOf(npmConfigPrefix) !== 0) {
return
}
const val = process.env[name]
if (name === npmConfigPrefix + 'loglevel') {
log.level = val
} else {
// add the user-defined options to the config
name = name.substring(npmConfigPrefix.length)
// gyp@741b7f1 enters an infinite loop when it encounters
// zero-length options so ensure those don't get through.
if (name) {
opts[name] = val
}
}
})

if (opts.loglevel) {
log.level = opts.loglevel
}

log.resume()

return { opts, argv, todo }
}

module.exports = parseArgv
module.exports.commands = commands
module.exports.aliases = aliases
module.exports.shorthands = shorthands
module.exports.configDefs = configDefs
Loading