"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/lib/configure.js" (11 Apr 2017, 15270 Bytes) of package /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 module.exports = exports = configure
    2 module.exports.test = { findAccessibleSync: findAccessibleSync,
    3   findPython: findPython }
    4 
    5 /**
    6  * Module dependencies.
    7  */
    8 
    9 var fs = require('graceful-fs')
   10   , path = require('path')
   11   , log = require('npmlog')
   12   , osenv = require('osenv')
   13   , which = require('which')
   14   , semver = require('semver')
   15   , mkdirp = require('mkdirp')
   16   , cp = require('child_process')
   17   , PathArray = require('path-array')
   18   , extend = require('util')._extend
   19   , processRelease = require('./process-release')
   20   , spawn = cp.spawn
   21   , execFile = cp.execFile
   22   , win = process.platform == 'win32'
   23   , findNodeDirectory = require('./find-node-directory')
   24   , msgFormat = require('util').format
   25 
   26 exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' for the current module'
   27 
   28 function configure (gyp, argv, callback) {
   29 
   30   var python = gyp.opts.python || process.env.PYTHON || 'python2'
   31     , buildDir = path.resolve('build')
   32     , configNames = [ 'config.gypi', 'common.gypi' ]
   33     , configs = []
   34     , nodeDir
   35     , release = processRelease(argv, gyp, process.version, process.release)
   36 
   37   findPython(python, function (err, found) {
   38     if (err) {
   39       callback(err)
   40     } else {
   41       python = found
   42       getNodeDir()
   43     }
   44   })
   45 
   46   function getNodeDir () {
   47 
   48     // 'python' should be set by now
   49     process.env.PYTHON = python
   50 
   51     if (gyp.opts.nodedir) {
   52       // --nodedir was specified. use that for the dev files
   53       nodeDir = gyp.opts.nodedir.replace(/^~/, osenv.home())
   54 
   55       log.verbose('get node dir', 'compiling against specified --nodedir dev files: %s', nodeDir)
   56       createBuildDir()
   57 
   58     } else {
   59       // if no --nodedir specified, ensure node dependencies are installed
   60       if ('v' + release.version !== process.version) {
   61         // if --target was given, then determine a target version to compile for
   62         log.verbose('get node dir', 'compiling against --target node version: %s', release.version)
   63       } else {
   64         // if no --target was specified then use the current host node version
   65         log.verbose('get node dir', 'no --target version specified, falling back to host node version: %s', release.version)
   66       }
   67 
   68       if (!release.semver) {
   69         // could not parse the version string with semver
   70         return callback(new Error('Invalid version number: ' + release.version))
   71       }
   72 
   73       // ensure that the target node version's dev files are installed
   74       gyp.opts.ensure = true
   75       gyp.commands.install([ release.version ], function (err, version) {
   76         if (err) return callback(err)
   77         log.verbose('get node dir', 'target node version installed:', release.versionDir)
   78         nodeDir = path.resolve(gyp.devDir, release.versionDir)
   79         createBuildDir()
   80       })
   81     }
   82   }
   83 
   84   function createBuildDir () {
   85     log.verbose('build dir', 'attempting to create "build" dir: %s', buildDir)
   86     mkdirp(buildDir, function (err, isNew) {
   87       if (err) return callback(err)
   88       log.verbose('build dir', '"build" dir needed to be created?', isNew)
   89       createConfigFile()
   90     })
   91   }
   92 
   93   function createConfigFile (err) {
   94     if (err) return callback(err)
   95 
   96     var configFilename = 'config.gypi'
   97     var configPath = path.resolve(buildDir, configFilename)
   98 
   99     log.verbose('build/' + configFilename, 'creating config file')
  100 
  101     var config = process.config || {}
  102       , defaults = config.target_defaults
  103       , variables = config.variables
  104 
  105     // default "config.variables"
  106     if (!variables) variables = config.variables = {}
  107 
  108     // default "config.defaults"
  109     if (!defaults) defaults = config.target_defaults = {}
  110 
  111     // don't inherit the "defaults" from node's `process.config` object.
  112     // doing so could cause problems in cases where the `node` executable was
  113     // compiled on a different machine (with different lib/include paths) than
  114     // the machine where the addon is being built to
  115     defaults.cflags = []
  116     defaults.defines = []
  117     defaults.include_dirs = []
  118     defaults.libraries = []
  119 
  120     // set the default_configuration prop
  121     if ('debug' in gyp.opts) {
  122       defaults.default_configuration = gyp.opts.debug ? 'Debug' : 'Release'
  123     }
  124     if (!defaults.default_configuration) {
  125       defaults.default_configuration = 'Release'
  126     }
  127 
  128     // set the target_arch variable
  129     variables.target_arch = gyp.opts.arch || process.arch || 'ia32'
  130 
  131     // set the node development directory
  132     variables.nodedir = nodeDir
  133 
  134     // don't copy dev libraries with nodedir option
  135     variables.copy_dev_lib = !gyp.opts.nodedir
  136 
  137     // disable -T "thin" static archives by default
  138     variables.standalone_static_library = gyp.opts.thin ? 0 : 1
  139 
  140     // loop through the rest of the opts and add the unknown ones as variables.
  141     // this allows for module-specific configure flags like:
  142     //
  143     //   $ node-gyp configure --shared-libxml2
  144     Object.keys(gyp.opts).forEach(function (opt) {
  145       if (opt === 'argv') return
  146       if (opt in gyp.configDefs) return
  147       variables[opt.replace(/-/g, '_')] = gyp.opts[opt]
  148     })
  149 
  150     // ensures that any boolean values from `process.config` get stringified
  151     function boolsToString (k, v) {
  152       if (typeof v === 'boolean')
  153         return String(v)
  154       return v
  155     }
  156 
  157     log.silly('build/' + configFilename, config)
  158 
  159     // now write out the config.gypi file to the build/ dir
  160     var prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step'
  161       , json = JSON.stringify(config, boolsToString, 2)
  162     log.verbose('build/' + configFilename, 'writing out config file: %s', configPath)
  163     configs.push(configPath)
  164     fs.writeFile(configPath, [prefix, json, ''].join('\n'), findConfigs)
  165   }
  166 
  167   function findConfigs (err) {
  168     if (err) return callback(err)
  169     var name = configNames.shift()
  170     if (!name) return runGyp()
  171     var fullPath = path.resolve(name)
  172     log.verbose(name, 'checking for gypi file: %s', fullPath)
  173     fs.stat(fullPath, function (err, stat) {
  174       if (err) {
  175         if (err.code == 'ENOENT') {
  176           findConfigs() // check next gypi filename
  177         } else {
  178           callback(err)
  179         }
  180       } else {
  181         log.verbose(name, 'found gypi file')
  182         configs.push(fullPath)
  183         findConfigs()
  184       }
  185     })
  186   }
  187 
  188   function runGyp (err) {
  189     if (err) return callback(err)
  190 
  191     if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
  192       if (win) {
  193         log.verbose('gyp', 'gyp format was not specified; forcing "msvs"')
  194         // force the 'make' target for non-Windows
  195         argv.push('-f', 'msvs')
  196       } else {
  197         log.verbose('gyp', 'gyp format was not specified; forcing "make"')
  198         // force the 'make' target for non-Windows
  199         argv.push('-f', 'make')
  200       }
  201     }
  202 
  203     function hasMsvsVersion () {
  204       return argv.some(function (arg) {
  205         return arg.indexOf('msvs_version') === 0
  206       })
  207     }
  208 
  209     if (win && !hasMsvsVersion()) {
  210       if ('msvs_version' in gyp.opts) {
  211         argv.push('-G', 'msvs_version=' + gyp.opts.msvs_version)
  212       } else {
  213         argv.push('-G', 'msvs_version=auto')
  214       }
  215     }
  216 
  217     // include all the ".gypi" files that were found
  218     configs.forEach(function (config) {
  219       argv.push('-I', config)
  220     })
  221 
  222     // for AIX we need to set up the path to the exp file
  223     // which contains the symbols needed for linking.
  224     // The file will either be in one of the following
  225     // depending on whether it is an installed or
  226     // development environment:
  227     //  - the include/node directory
  228     //  - the out/Release directory
  229     //  - the out/Debug directory
  230     //  - the root directory
  231     var node_exp_file = undefined
  232     if (process.platform === 'aix') {
  233       var node_root_dir = findNodeDirectory()
  234       var candidates = ['include/node/node.exp',
  235                         'out/Release/node.exp',
  236                         'out/Debug/node.exp',
  237                         'node.exp']
  238       var logprefix = 'find exports file'
  239       node_exp_file = findAccessibleSync(logprefix, node_root_dir, candidates)
  240       if (node_exp_file !== undefined) {
  241         log.verbose(logprefix, 'Found exports file: %s', node_exp_file)
  242       } else {
  243         var msg = msgFormat('Could not find node.exp file in %s', node_root_dir)
  244         log.error(logprefix, 'Could not find exports file')
  245         return callback(new Error(msg))
  246       }
  247     }
  248 
  249     // this logic ported from the old `gyp_addon` python file
  250     var gyp_script = path.resolve(__dirname, '..', 'gyp', 'gyp_main.py')
  251     var addon_gypi = path.resolve(__dirname, '..', 'addon.gypi')
  252     var common_gypi = path.resolve(nodeDir, 'include/node/common.gypi')
  253     fs.stat(common_gypi, function (err, stat) {
  254       if (err)
  255         common_gypi = path.resolve(nodeDir, 'common.gypi')
  256 
  257       var output_dir = 'build'
  258       if (win) {
  259         // Windows expects an absolute path
  260         output_dir = buildDir
  261       }
  262       var nodeGypDir = path.resolve(__dirname, '..')
  263 
  264       argv.push('-I', addon_gypi)
  265       argv.push('-I', common_gypi)
  266       argv.push('-Dlibrary=shared_library')
  267       argv.push('-Dvisibility=default')
  268       argv.push('-Dnode_root_dir=' + nodeDir)
  269       if (process.platform === 'aix') {
  270         argv.push('-Dnode_exp_file=' + node_exp_file)
  271       }
  272       argv.push('-Dnode_gyp_dir=' + nodeGypDir)
  273       argv.push('-Dnode_lib_file=' + release.name + '.lib')
  274       argv.push('-Dmodule_root_dir=' + process.cwd())
  275       argv.push('--depth=.')
  276       argv.push('--no-parallel')
  277 
  278       // tell gyp to write the Makefile/Solution files into output_dir
  279       argv.push('--generator-output', output_dir)
  280 
  281       // tell make to write its output into the same dir
  282       argv.push('-Goutput_dir=.')
  283 
  284       // enforce use of the "binding.gyp" file
  285       argv.unshift('binding.gyp')
  286 
  287       // execute `gyp` from the current target nodedir
  288       argv.unshift(gyp_script)
  289 
  290       // make sure python uses files that came with this particular node package
  291       var pypath = new PathArray(process.env, 'PYTHONPATH')
  292       pypath.unshift(path.join(__dirname, '..', 'gyp', 'pylib'))
  293 
  294       var cp = gyp.spawn(python, argv)
  295       cp.on('exit', onCpExit)
  296     })
  297   }
  298 
  299   /**
  300    * Called when the `gyp` child process exits.
  301    */
  302 
  303   function onCpExit (code, signal) {
  304     if (code !== 0) {
  305       callback(new Error('`gyp` failed with exit code: ' + code))
  306     } else {
  307       // we're done
  308       callback()
  309     }
  310   }
  311 
  312 }
  313 
  314 /**
  315  * Returns the first file or directory from an array of candidates that is
  316  * readable by the current user, or undefined if none of the candidates are
  317  * readable.
  318  */
  319 function findAccessibleSync (logprefix, dir, candidates) {
  320   for (var next = 0; next < candidates.length; next++) {
  321      var candidate = path.resolve(dir, candidates[next])
  322      try {
  323        var fd = fs.openSync(candidate, 'r')
  324      } catch (e) {
  325        // this candidate was not found or not readable, do nothing
  326        log.silly(logprefix, 'Could not open %s: %s', candidate, e.message)
  327        continue
  328      }
  329      fs.closeSync(fd)
  330      log.silly(logprefix, 'Found readable %s', candidate)
  331      return candidate
  332   }
  333 
  334   return undefined
  335 }
  336 
  337 function findPython (python, callback) {
  338   checkPython()
  339 
  340   // Check if Python is in the $PATH
  341   function checkPython () {
  342     log.verbose('check python', 'checking for Python executable "%s" in the PATH', python)
  343     which(python, function (err, execPath) {
  344       if (err) {
  345         log.verbose('`which` failed', python, err)
  346         if (python === 'python2') {
  347           python = 'python'
  348           return checkPython()
  349         }
  350         if (win) {
  351           checkPythonLauncher()
  352         } else {
  353           failNoPython()
  354         }
  355       } else {
  356         log.verbose('`which` succeeded', python, execPath)
  357         // Found the `python` exceutable, and from now on we use it explicitly.
  358         // This solves #667 and #750 (`execFile` won't run batch files
  359         // (*.cmd, and *.bat))
  360         python = execPath
  361         checkPythonVersion()
  362       }
  363     })
  364   }
  365 
  366   // Distributions of Python on Windows by default install with the "py.exe"
  367   // Python launcher which is more likely to exist than the Python executable
  368   // being in the $PATH.
  369   // Because the Python launcher supports all versions of Python, we have to
  370   // explicitly request a Python 2 version. This is done by supplying "-2" as
  371   // the first command line argument. Since "py.exe -2" would be an invalid
  372   // executable for "execFile", we have to use the launcher to figure out
  373   // where the actual "python.exe" executable is located.
  374   function checkPythonLauncher () {
  375     log.verbose('could not find "' + python + '". checking python launcher')
  376     var env = extend({}, process.env)
  377     env.TERM = 'dumb'
  378 
  379     var launcherArgs = ['-2', '-c', 'import sys; print sys.executable']
  380     execFile('py.exe', launcherArgs, { env: env }, function (err, stdout) {
  381       if (err) {
  382         guessPython()
  383         return
  384       }
  385       python = stdout.trim()
  386       log.verbose('check python launcher', 'python executable found: %j', python)
  387       checkPythonVersion()
  388     })
  389   }
  390 
  391   // Called on Windows when "python" isn't available in the current $PATH.
  392   // We're gonna check if "%SystemDrive%\python27\python.exe" exists.
  393   function guessPython () {
  394     log.verbose('could not find "' + python + '". guessing location')
  395     var rootDir = process.env.SystemDrive || 'C:\\'
  396     if (rootDir[rootDir.length - 1] !== '\\') {
  397       rootDir += '\\'
  398     }
  399     var pythonPath = path.resolve(rootDir, 'Python27', 'python.exe')
  400     log.verbose('ensuring that file exists:', pythonPath)
  401     fs.stat(pythonPath, function (err, stat) {
  402       if (err) {
  403         if (err.code == 'ENOENT') {
  404           failNoPython()
  405         } else {
  406           callback(err)
  407         }
  408         return
  409       }
  410       python = pythonPath
  411       checkPythonVersion()
  412     })
  413   }
  414 
  415   function checkPythonVersion () {
  416     var env = extend({}, process.env)
  417     env.TERM = 'dumb'
  418 
  419     execFile(python, ['-c', 'import platform; print(platform.python_version());'], { env: env }, function (err, stdout) {
  420       if (err) {
  421         return callback(err)
  422       }
  423       log.verbose('check python version', '`%s -c "import platform; print(platform.python_version());"` returned: %j', python, stdout)
  424       var version = stdout.trim()
  425       if (~version.indexOf('+')) {
  426         log.silly('stripping "+" sign(s) from version')
  427         version = version.replace(/\+/g, '')
  428       }
  429       if (~version.indexOf('rc')) {
  430         log.silly('stripping "rc" identifier from version')
  431         version = version.replace(/rc(.*)$/ig, '')
  432       }
  433       var range = semver.Range('>=2.5.0 <3.0.0')
  434       var valid = false
  435       try {
  436         valid = range.test(version)
  437       } catch (e) {
  438         log.silly('range.test() error', e)
  439       }
  440       if (valid) {
  441         callback(null, python)
  442       } else {
  443         failPythonVersion(version)
  444       }
  445     })
  446   }
  447 
  448   function failNoPython () {
  449     callback(new Error('Can\'t find Python executable "' + python +
  450           '", you can set the PYTHON env variable.'))
  451   }
  452 
  453   function failPythonVersion (badVersion) {
  454     callback(new Error('Python executable "' + python +
  455           '" is v' + badVersion + ', which is not supported by gyp.\n' +
  456           'You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.'))
  457   }
  458 }