mirror of
https://github.com/electron/node-gyp.git
synced 2025-08-15 12:58:19 +02:00

* lib: create a Python symlink and add it to PATH Helps to ensure a version of Python validated by lib/find-python.js is used to run various Python scripts generated by gyp. Known to affect gyp-mac-tool, probably affects gyp-flock-tool as well. These Python scripts (such as `gyp-mac-tool`) are invoked directly, via the generated Makefile, so their shebang lines determine which Python binary is used to run them. The shebang lines of these scripts are all `#!/usr/bin/env python3`, so the first `python3` on the user's PATH will be used. By adding a symlink to the Python binary validated by find-python.js, and putting this symlink first on the PATH, we can ensure we use a compatible version of Python to run these scripts. (Only on Unix/Unix-like OSes. Symlinks are tricky on Windows, and Python isn't used at build-time anyhow on Windows, so this intervention isn't useful or necessary on Windows. A similar technique for Windows, no symlinks required, would be to make batch scripts which execute the target binary, much like what Node does for its bundled copy of npm on Windows.) * test: update mocked graceful-fs for configure test Add missing functions "unlink()" and "symlink()" to mocked module. * lib: log any errors when creating Python symlink Warn users about errors, but continue on in case the user does happen to have new enough Python on their PATH. (The symlinks are only meant to fix an issue in a corner case, where the user told `node-gyp` where new enough Python is, but it's not the first `python3` on their PATH. We should not introduce a new potential failure mode to all users when fixing this bug. So no hard errors during the symlink process.) * lib: improve error formatting for Python symlink Logging the entire error object shows the stack twice, and all the other information is contained in the stack. It also messes with the order of what is logged. Rather than logging a bunch of redundant information in a messy way, we can log only the stack. Logging it in a separate log.warn() also gets rid of an extra space character at the beginning of the line. * lib: restore err.errno to logs for symlink errors This info (err.errno) is the only piece of information in the error object that is not redundant to err.stack. * lib: use log.verbose, not log.warn These messages aren't important enough to be `log.warn`s. Log as verbose only; they will also appear in full error output.
211 lines
5.7 KiB
JavaScript
211 lines
5.7 KiB
JavaScript
'use strict'
|
|
|
|
const fs = require('graceful-fs')
|
|
const path = require('path')
|
|
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'
|
|
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
|
|
})
|
|
}
|
|
|
|
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()
|
|
|
|
/**
|
|
* Load the "config.gypi" file that was generated during "configure".
|
|
*/
|
|
|
|
function loadConfigGypi () {
|
|
var 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
|
|
|
|
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)
|
|
|
|
if (win) {
|
|
findSolutionFile()
|
|
} else {
|
|
doWhich()
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Uses node-which to locate the msbuild / make executable.
|
|
*/
|
|
|
|
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`.'))
|
|
}
|
|
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
|
|
}
|
|
log.verbose('`which` succeeded for `' + command + '`', execPath)
|
|
doBuild()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Actually spawn the process and compile the module.
|
|
*/
|
|
|
|
function doBuild () {
|
|
// Enable Verbose build
|
|
var verbose = log.levels[log.level] <= log.levels.verbose
|
|
var j
|
|
|
|
if (!win && verbose) {
|
|
argv.push('V=1')
|
|
}
|
|
|
|
if (win && !verbose) {
|
|
argv.push('/clp:Verbosity=minimal')
|
|
}
|
|
|
|
if (win) {
|
|
// Turn off the Microsoft logo on Windows
|
|
argv.push('/nologo')
|
|
}
|
|
|
|
// Specify the build type, Release by default
|
|
if (win) {
|
|
// 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'
|
|
: (archLower === 'arm' ? 'ARM'
|
|
: (archLower === 'arm64' ? 'ARM64' : 'Win32'))
|
|
argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
|
|
if (jobs) {
|
|
j = parseInt(jobs, 10)
|
|
if (!isNaN(j) && j > 0) {
|
|
argv.push('/m:' + j)
|
|
} else if (jobs.toUpperCase() === 'MAX') {
|
|
argv.push('/m:' + require('os').cpus().length)
|
|
}
|
|
}
|
|
} else {
|
|
argv.push('BUILDTYPE=' + buildType)
|
|
// Invoke the Makefile in the 'build' dir.
|
|
argv.push('-C')
|
|
argv.push('build')
|
|
if (jobs) {
|
|
j = parseInt(jobs, 10)
|
|
if (!isNaN(j) && j > 0) {
|
|
argv.push('--jobs')
|
|
argv.push(j)
|
|
} else if (jobs.toUpperCase() === 'MAX') {
|
|
argv.push('--jobs')
|
|
argv.push(require('os').cpus().length)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (win) {
|
|
// did the user specify their own .sln file?
|
|
var hasSln = argv.some(function (arg) {
|
|
return path.extname(arg) === '.sln'
|
|
})
|
|
if (!hasSln) {
|
|
argv.unshift(gyp.opts.solution || guessedSolution)
|
|
}
|
|
}
|
|
|
|
if (!win) {
|
|
// Add build-time dependency symlinks (such as Python) to PATH
|
|
const buildBinsDir = path.resolve('build', 'node_gyp_bins')
|
|
process.env.PATH = `${buildBinsDir}:${process.env.PATH}`
|
|
log.verbose('bin symlinks', `adding symlinks (such as Python), at "${buildBinsDir}", to PATH`)
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|
|
|
|
module.exports = build
|
|
module.exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
|