@@ -4,15 +4,12 @@ const fs = require('graceful-fs')
4
4
const path = require ( 'path' )
5
5
const log = require ( 'npmlog' )
6
6
const os = require ( 'os' )
7
- const semver = require ( 'semver' )
8
7
const mkdirp = require ( 'mkdirp' )
9
- const cp = require ( 'child_process' )
10
- const extend = require ( 'util' ) . _extend // eslint-disable-line
11
8
const processRelease = require ( './process-release' )
12
9
const win = process . platform === 'win32'
13
10
const findNodeDirectory = require ( './find-node-directory' )
14
11
const msgFormat = require ( 'util' ) . format
15
- const logWithPrefix = require ( './util' ) . logWithPrefix
12
+ var findPython = require ( './find-python' )
16
13
if ( win ) {
17
14
var findVisualStudio = require ( './find-visualstudio' )
18
15
}
@@ -375,287 +372,8 @@ function findAccessibleSync (logprefix, dir, candidates) {
375
372
return undefined
376
373
}
377
374
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
-
655
375
module . exports = configure
656
376
module . exports . test = {
657
- PythonFinder : PythonFinder ,
658
- findAccessibleSync : findAccessibleSync ,
659
- findPython : findPython
377
+ findAccessibleSync : findAccessibleSync
660
378
}
661
379
module . exports . usage = 'Generates ' + ( win ? 'MSVC project files' : 'a Makefile' ) + ' for the current module'
0 commit comments