]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/node-gyp/lib/configure.js
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / node_modules / node-gyp / lib / configure.js
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 }