@@ -4,15 +4,12 @@ const fs = require('graceful-fs')
44const path = require ( 'path' )
55const log = require ( 'npmlog' )
66const os = require ( 'os' )
7- const semver = require ( 'semver' )
87const mkdirp = require ( 'mkdirp' )
9- const cp = require ( 'child_process' )
10- const extend = require ( 'util' ) . _extend // eslint-disable-line
118const processRelease = require ( './process-release' )
129const win = process . platform === 'win32'
1310const findNodeDirectory = require ( './find-node-directory' )
1411const msgFormat = require ( 'util' ) . format
15- const logWithPrefix = require ( './util' ) . logWithPrefix
12+ var findPython = require ( './find-python' )
1613if ( win ) {
1714 var findVisualStudio = require ( './find-visualstudio' )
1815}
@@ -375,287 +372,8 @@ function findAccessibleSync (logprefix, dir, candidates) {
375372 return undefined
376373}
377374
378- function PythonFinder ( configPython , callback ) {
379- this . callback = callback
380- this . configPython = configPython
381- this . errorLog = [ ]
382- }
383-
384- PythonFinder . prototype = {
385- log : logWithPrefix ( log , 'find Python' ) ,
386- argsExecutable : [ '-c' , 'import sys; print(sys.executable);' ] ,
387- argsVersion : [ '-c' , 'import sys; print("%s.%s.%s" % sys.version_info[:3]);' ] ,
388- semverRange : '>=2.6.0 <3.0.0' ,
389-
390- // These can be overridden for testing:
391- execFile : cp . execFile ,
392- env : process . env ,
393- win : win ,
394- pyLauncher : 'py.exe' ,
395- defaultLocation : path . join ( process . env . SystemDrive || 'C:' , 'Python27' ,
396- 'python.exe' ) ,
397-
398- // Logs a message at verbose level, but also saves it to be displayed later
399- // at error level if an error occurs. This should help diagnose the problem.
400- addLog : function addLog ( message ) {
401- this . log . verbose ( message )
402- this . errorLog . push ( message )
403- } ,
404-
405- // Find Python by trying a sequence of possibilities.
406- // Ignore errors, keep trying until Python is found.
407- findPython : function findPython ( ) {
408- const SKIP = 0 ; const FAIL = 1
409- const toCheck = [
410- {
411- before : ( ) => {
412- if ( ! this . configPython ) {
413- this . addLog (
414- 'Python is not set from command line or npm configuration' )
415- return SKIP
416- }
417- this . addLog ( 'checking Python explicitly set from command line or ' +
418- 'npm configuration' )
419- this . addLog ( '- "--python=" or "npm config get python" is ' +
420- `"${ this . configPython } "` )
421- } ,
422- check : this . checkCommand ,
423- arg : this . configPython
424- } ,
425- {
426- before : ( ) => {
427- if ( ! this . env . PYTHON ) {
428- this . addLog ( 'Python is not set from environment variable PYTHON' )
429- return SKIP
430- }
431- this . addLog (
432- 'checking Python explicitly set from environment variable PYTHON' )
433- this . addLog ( `- process.env.PYTHON is "${ this . env . PYTHON } "` )
434- } ,
435- check : this . checkCommand ,
436- arg : this . env . PYTHON
437- } ,
438- {
439- before : ( ) => { this . addLog ( 'checking if "python2" can be used' ) } ,
440- check : this . checkCommand ,
441- arg : 'python2'
442- } ,
443- {
444- before : ( ) => { this . addLog ( 'checking if "python" can be used' ) } ,
445- check : this . checkCommand ,
446- arg : 'python'
447- } ,
448- {
449- before : ( ) => {
450- if ( ! this . win ) {
451- // Everything after this is Windows specific
452- return FAIL
453- }
454- this . addLog (
455- 'checking if the py launcher can be used to find Python 2' )
456- } ,
457- check : this . checkPyLauncher
458- } ,
459- {
460- before : ( ) => {
461- this . addLog (
462- 'checking if Python 2 is installed in the default location' )
463- } ,
464- check : this . checkExecPath ,
465- arg : this . defaultLocation
466- }
467- ]
468-
469- function runChecks ( err ) {
470- this . log . silly ( 'runChecks: err = %j' , ( err && err . stack ) || err )
471-
472- const check = toCheck . shift ( )
473- if ( ! check ) {
474- return this . fail ( )
475- }
476-
477- const before = check . before . apply ( this )
478- if ( before === SKIP ) {
479- return runChecks . apply ( this )
480- }
481- if ( before === FAIL ) {
482- return this . fail ( )
483- }
484-
485- const args = [ runChecks . bind ( this ) ]
486- if ( check . arg ) {
487- args . unshift ( check . arg )
488- }
489- check . check . apply ( this , args )
490- }
491-
492- runChecks . apply ( this )
493- } ,
494-
495- // Check if command is a valid Python to use.
496- // Will exit the Python finder on success.
497- // If on Windows, run in a CMD shell to support BAT/CMD launchers.
498- checkCommand : function checkCommand ( command , errorCallback ) {
499- var exec = command
500- var args = this . argsExecutable
501- var shell = false
502- if ( this . win ) {
503- // Arguments have to be manually quoted
504- exec = `"${ exec } "`
505- args = args . map ( a => `"${ a } "` )
506- shell = true
507- }
508-
509- this . log . verbose ( `- executing "${ command } " to get executable path` )
510- this . run ( exec , args , shell , function ( err , execPath ) {
511- // Possible outcomes:
512- // - Error: not in PATH, not executable or execution fails
513- // - Gibberish: the next command to check version will fail
514- // - Absolute path to executable
515- if ( err ) {
516- this . addLog ( `- "${ command } " is not in PATH or produced an error` )
517- return errorCallback ( err )
518- }
519- this . addLog ( `- executable path is "${ execPath } "` )
520- this . checkExecPath ( execPath , errorCallback )
521- } . bind ( this ) )
522- } ,
523-
524- // Check if the py launcher can find a valid Python to use.
525- // Will exit the Python finder on success.
526- // Distributions of Python on Windows by default install with the "py.exe"
527- // Python launcher which is more likely to exist than the Python executable
528- // being in the $PATH.
529- // Because the Python launcher supports all versions of Python, we have to
530- // explicitly request a Python 2 version. This is done by supplying "-2" as
531- // the first command line argument. Since "py.exe -2" would be an invalid
532- // executable for "execFile", we have to use the launcher to figure out
533- // where the actual "python.exe" executable is located.
534- checkPyLauncher : function checkPyLauncher ( errorCallback ) {
535- this . log . verbose (
536- `- executing "${ this . pyLauncher } " to get Python 2 executable path` )
537- this . run ( this . pyLauncher , [ '-2' , ...this . argsExecutable ] , false ,
538- function ( err , execPath ) {
539- // Possible outcomes: same as checkCommand
540- if ( err ) {
541- this . addLog (
542- `- "${ this . pyLauncher } " is not in PATH or produced an error` )
543- return errorCallback ( err )
544- }
545- this . addLog ( `- executable path is "${ execPath } "` )
546- this . checkExecPath ( execPath , errorCallback )
547- } . bind ( this ) )
548- } ,
549-
550- // Check if a Python executable is the correct version to use.
551- // Will exit the Python finder on success.
552- checkExecPath : function checkExecPath ( execPath , errorCallback ) {
553- this . log . verbose ( `- executing "${ execPath } " to get version` )
554- this . run ( execPath , this . argsVersion , false , function ( err , version ) {
555- // Possible outcomes:
556- // - Error: executable can not be run (likely meaning the command wasn't
557- // a Python executable and the previous command produced gibberish)
558- // - Gibberish: somehow the last command produced an executable path,
559- // this will fail when verifying the version
560- // - Version of the Python executable
561- if ( err ) {
562- this . addLog ( `- "${ execPath } " could not be run` )
563- return errorCallback ( err )
564- }
565- this . addLog ( `- version is "${ version } "` )
566-
567- const range = semver . Range ( this . semverRange )
568- var valid = false
569- try {
570- valid = range . test ( version )
571- } catch ( err ) {
572- this . log . silly ( 'range.test() threw:\n%s' , err . stack )
573- this . addLog ( `- "${ execPath } " does not have a valid version` )
574- this . addLog ( '- is it a Python executable?' )
575- return errorCallback ( err )
576- }
577-
578- if ( ! valid ) {
579- this . addLog ( `- version is ${ version } - should be ${ this . semverRange } ` )
580- this . addLog ( '- THIS VERSION OF PYTHON IS NOT SUPPORTED' )
581- return errorCallback ( new Error (
582- `Found unsupported Python version ${ version } ` ) )
583- }
584- this . succeed ( execPath , version )
585- } . bind ( this ) )
586- } ,
587-
588- // Run an executable or shell command, trimming the output.
589- run : function run ( exec , args , shell , callback ) {
590- var env = extend ( { } , this . env )
591- env . TERM = 'dumb'
592- const opts = { env : env , shell : shell }
593-
594- this . log . silly ( 'execFile: exec = %j' , exec )
595- this . log . silly ( 'execFile: args = %j' , args )
596- this . log . silly ( 'execFile: opts = %j' , opts )
597- try {
598- this . execFile ( exec , args , opts , execFileCallback . bind ( this ) )
599- } catch ( err ) {
600- this . log . silly ( 'execFile: threw:\n%s' , err . stack )
601- return callback ( err )
602- }
603-
604- function execFileCallback ( err , stdout , stderr ) {
605- this . log . silly ( 'execFile result: err = %j' , ( err && err . stack ) || err )
606- this . log . silly ( 'execFile result: stdout = %j' , stdout )
607- this . log . silly ( 'execFile result: stderr = %j' , stderr )
608- if ( err ) {
609- return callback ( err )
610- }
611- const execPath = stdout . trim ( )
612- callback ( null , execPath )
613- }
614- } ,
615-
616- succeed : function succeed ( execPath , version ) {
617- this . log . info ( `using Python version ${ version } found at "${ execPath } "` )
618- process . nextTick ( this . callback . bind ( null , null , execPath ) )
619- } ,
620-
621- fail : function fail ( ) {
622- const errorLog = this . errorLog . join ( '\n' )
623-
624- const pathExample = this . win ? 'C:\\Path\\To\\python.exe'
625- : '/path/to/pythonexecutable'
626- // For Windows 80 col console, use up to the column before the one marked
627- // with X (total 79 chars including logger prefix, 58 chars usable here):
628- // X
629- const info = [
630- '**********************************************************' ,
631- 'You need to install the latest version of Python 2.7.' ,
632- 'Node-gyp should be able to find and use Python. If not,' ,
633- 'you can try one of the following options:' ,
634- `- Use the switch --python="${ pathExample } "` ,
635- ' (accepted by both node-gyp and npm)' ,
636- '- Set the environment variable PYTHON' ,
637- '- Set the npm configuration variable python:' ,
638- ` npm config set python "${ pathExample } "` ,
639- 'For more information consult the documentation at:' ,
640- 'https://github.com/nodejs/node-gyp#installation' ,
641- '**********************************************************'
642- ] . join ( '\n' )
643-
644- this . log . error ( `\n${ errorLog } \n\n${ info } \n` )
645- process . nextTick ( this . callback . bind ( null , new Error (
646- 'Could not find any Python 2 installation to use' ) ) )
647- }
648- }
649-
650- function findPython ( configPython , callback ) {
651- var finder = new PythonFinder ( configPython , callback )
652- finder . findPython ( )
653- }
654-
655375module . exports = configure
656376module . exports . test = {
657- PythonFinder : PythonFinder ,
658- findAccessibleSync : findAccessibleSync ,
659- findPython : findPython
377+ findAccessibleSync : findAccessibleSync
660378}
661379module . exports . usage = 'Generates ' + ( win ? 'MSVC project files' : 'a Makefile' ) + ' for the current module'
0 commit comments