'use strict' const fs = require('graceful-fs').promises const log = require('./log') const path = require('path') function parseConfigGypi (config) { // translated from tools/js2c.py of Node.js // 1. string comments config = config.replace(/#.*/g, '') // 2. join multiline strings config = config.replace(/'$\s+'/mg, '') // 3. normalize string literals from ' into " config = config.replace(/'/g, '"') return JSON.parse(config) } async function getBaseConfigGypi ({ gyp, nodeDir }) { // try reading $nodeDir/include/node/config.gypi first when: // 1. --dist-url or --nodedir is specified // 2. and --force-process-config is not specified const useCustomHeaders = gyp.opts.nodedir || gyp.opts.disturl || gyp.opts['dist-url'] const shouldReadConfigGypi = useCustomHeaders && !gyp.opts['force-process-config'] if (shouldReadConfigGypi && nodeDir) { try { const baseConfigGypiPath = path.resolve(nodeDir, 'include/node/config.gypi') const baseConfigGypi = await fs.readFile(baseConfigGypiPath) return parseConfigGypi(baseConfigGypi.toString()) } catch (err) { log.warn('read config.gypi', err.message) } } // fallback to process.config if it is invalid return JSON.parse(JSON.stringify(process.config)) } async function getCurrentConfigGypi ({ gyp, nodeDir, vsInfo, python }) { const config = await getBaseConfigGypi({ gyp, nodeDir }) if (!config.target_defaults) { config.target_defaults = {} } if (!config.variables) { config.variables = {} } const defaults = config.target_defaults const variables = config.variables // don't inherit the "defaults" from the base config.gypi. // doing so could cause problems in cases where the `node` executable was // compiled on a different machine (with different lib/include paths) than // the machine where the addon is being built to defaults.cflags = [] defaults.defines = [] defaults.include_dirs = [] defaults.libraries = [] // set the default_configuration prop if ('debug' in gyp.opts) { defaults.default_configuration = gyp.opts.debug ? 'Debug' : 'Release' } if (!defaults.default_configuration) { defaults.default_configuration = 'Release' } // set the target_arch variable variables.target_arch = gyp.opts.arch || process.arch || 'ia32' if (variables.target_arch === 'arm64') { defaults.msvs_configuration_platform = 'ARM64' defaults.xcode_configuration_platform = 'arm64' } // set the node development directory variables.nodedir = nodeDir // set the configured Python path variables.python = python // disable -T "thin" static archives by default variables.standalone_static_library = gyp.opts.thin ? 0 : 1 if (process.platform === 'win32') { defaults.msbuild_toolset = vsInfo.toolset if (vsInfo.sdk) { defaults.msvs_windows_target_platform_version = vsInfo.sdk } if (variables.target_arch === 'arm64') { if (vsInfo.versionMajor > 15 || (vsInfo.versionMajor === 15 && vsInfo.versionMajor >= 9)) { defaults.msvs_enable_marmasm = 1 } else { log.warn('Compiling ARM64 assembly is only available in\n' + 'Visual Studio 2017 version 15.9 and above') } } variables.msbuild_path = vsInfo.msBuild } // loop through the rest of the opts and add the unknown ones as variables. // this allows for module-specific configure flags like: // // $ node-gyp configure --shared-libxml2 Object.keys(gyp.opts).forEach(function (opt) { if (opt === 'argv') { return } if (opt in gyp.configDefs) { return } variables[opt.replace(/-/g, '_')] = gyp.opts[opt] }) return config } async function createConfigGypi ({ gyp, buildDir, nodeDir, vsInfo, python }) { const configFilename = 'config.gypi' const configPath = path.resolve(buildDir, configFilename) log.verbose('build/' + configFilename, 'creating config file') const config = await getCurrentConfigGypi({ gyp, nodeDir, vsInfo, python }) // ensures that any boolean values in config.gypi get stringified function boolsToString (k, v) { if (typeof v === 'boolean') { return String(v) } return v } log.silly('build/' + configFilename, config) // now write out the config.gypi file to the build/ dir const prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step' const json = JSON.stringify(config, boolsToString, 2) log.verbose('build/' + configFilename, 'writing out config file: %s', configPath) await fs.writeFile(configPath, [prefix, json, ''].join('\n')) return configPath } module.exports = { createConfigGypi, parseConfigGypi, getCurrentConfigGypi }