@@ -11,10 +11,14 @@ const request = require('request')
11
11
const processRelease = require ( './process-release' )
12
12
const win = process . platform === 'win32'
13
13
const getProxyFromURI = require ( './proxy' )
14
+ const { spawn } = require ( 'child_process' )
14
15
15
16
function install ( fs , gyp , argv , callback ) {
16
17
var release = processRelease ( argv , gyp , process . version , process . release )
17
18
19
+ const ninjaPath = gyp . opts [ 'ninja-path' ] || path . resolve ( gyp . devDir , 'ninja' )
20
+ const ninjaUrl = 'https://github.com/ninja-build/ninja/archive/v1.10.0.tar.gz'
21
+
18
22
// ensure no double-callbacks happen
19
23
function cb ( err ) {
20
24
if ( cb . done ) {
@@ -62,42 +66,62 @@ function install (fs, gyp, argv, callback) {
62
66
// the directory where the dev files will be installed
63
67
var devDir = path . resolve ( gyp . devDir , release . versionDir )
64
68
65
- // If '--ensure' was passed, then don't *always* install the version;
66
- // check if it is already installed, and only install when needed
67
- if ( gyp . opts . ensure ) {
68
- log . verbose ( 'install' , '--ensure was passed, so won\'t reinstall if already installed' )
69
- fs . stat ( devDir , function ( err ) {
70
- if ( err ) {
71
- if ( err . code === 'ENOENT' ) {
72
- log . verbose ( 'install' , 'version not already installed, continuing with install' , release . version )
73
- go ( )
74
- } else if ( err . code === 'EACCES' ) {
75
- eaccesFallback ( err )
76
- } else {
77
- cb ( err )
78
- }
79
- return
80
- }
81
- log . verbose ( 'install' , 'version is already installed, need to check "installVersion"' )
82
- var installVersionFile = path . resolve ( devDir , 'installVersion' )
83
- fs . readFile ( installVersionFile , 'ascii' , function ( err , ver ) {
84
- if ( err && err . code !== 'ENOENT' ) {
85
- return cb ( err )
86
- }
87
- var installVersion = parseInt ( ver , 10 ) || 0
88
- log . verbose ( 'got "installVersion"' , installVersion )
89
- log . verbose ( 'needs "installVersion"' , gyp . package . installVersion )
90
- if ( installVersion < gyp . package . installVersion ) {
91
- log . verbose ( 'install' , 'version is no good; reinstalling' )
92
- go ( )
93
- } else {
94
- log . verbose ( 'install' , 'version is good' )
95
- cb ( )
69
+ function handleInstall ( err ) {
70
+ if ( err ) cb ( err )
71
+
72
+ // If '--ensure' was passed, then don't *always* install the version;
73
+ // check if it is already installed, and only install when needed
74
+ if ( gyp . opts . ensure ) {
75
+ log . verbose ( 'install' , '--ensure was passed, so won\'t reinstall if already installed' )
76
+ fs . stat ( devDir , function ( err ) {
77
+ if ( err ) {
78
+ if ( err . code === 'ENOENT' ) {
79
+ log . verbose ( 'install' , 'version not already installed, continuing with install' , release . version )
80
+ go ( )
81
+ } else if ( err . code === 'EACCES' ) {
82
+ eaccesFallback ( err )
83
+ } else {
84
+ cb ( err )
85
+ }
86
+ return
96
87
}
88
+ log . verbose ( 'install' , 'version is already installed, need to check "installVersion"' )
89
+ var installVersionFile = path . resolve ( devDir , 'installVersion' )
90
+ fs . readFile ( installVersionFile , 'ascii' , function ( err , ver ) {
91
+ if ( err && err . code !== 'ENOENT' ) {
92
+ return cb ( err )
93
+ }
94
+ var installVersion = parseInt ( ver , 10 ) || 0
95
+ log . verbose ( 'got "installVersion"' , installVersion )
96
+ log . verbose ( 'needs "installVersion"' , gyp . package . installVersion )
97
+ if ( installVersion < gyp . package . installVersion ) {
98
+ log . verbose ( 'install' , 'version is no good; reinstalling' )
99
+ go ( )
100
+ } else {
101
+ log . verbose ( 'install' , 'version is good' )
102
+ cb ( )
103
+ }
104
+ } )
97
105
} )
106
+ } else {
107
+ go ( )
108
+ }
109
+ }
110
+
111
+ if ( gyp . opts . ninja ) {
112
+ downloadNinja ( ( err , alreadyExists ) => {
113
+ if ( err ) return cb ( err )
114
+
115
+ if ( alreadyExists ) {
116
+ log . verbose ( 'ninja' , 'appears to be previously installed' )
117
+ handleInstall ( )
118
+ } else {
119
+ log . verbose ( 'ninja' , 'building...' )
120
+ buildNinja ( handleInstall )
121
+ }
98
122
} )
99
123
} else {
100
- go ( )
124
+ handleInstall ( )
101
125
}
102
126
103
127
function getContentSha ( res , callback ) {
@@ -297,7 +321,7 @@ function install (fs, gyp, argv, callback) {
297
321
}
298
322
299
323
function downloadNodeLib ( done ) {
300
- log . verbose ( 'on Windows; need to download `' + release . name + '.lib`...' )
324
+ log . verbose ( 'on Windows need to download `' + release . name + '.lib`...' )
301
325
var archs = [ 'ia32' , 'x64' , 'arm64' ]
302
326
var async = archs . length
303
327
archs . forEach ( function ( arch ) {
@@ -352,6 +376,60 @@ function install (fs, gyp, argv, callback) {
352
376
} ) // mkdir()
353
377
} // go()
354
378
379
+ function downloadNinja ( done ) {
380
+ if ( fs . existsSync ( ninjaPath ) && gyp . opts . ensure ) {
381
+ return done ( null , true )
382
+ }
383
+
384
+ log . verbose ( 'ninja' , `url: ${ ninjaUrl } ` )
385
+ log . verbose ( 'ninja' , 'downloading...' )
386
+
387
+ request ( { strictSSL : false , url : ninjaUrl } )
388
+ . on ( 'response' , ( res ) => {
389
+ if ( res . statusCode !== 200 ) {
390
+ const error = new Error ( `Ninja failed downloading tarball: ${ res . statusCode } ` )
391
+ return done ( error )
392
+ }
393
+
394
+ log . verbose ( 'ninja' , `http status: ${ res . statusCode } ` )
395
+ function logger ( path ) {
396
+ const name = path . substring ( ninjaPath . length + 1 ) . replace ( / [ . ] + ?\/ / , '' )
397
+ if ( name . length > 0 ) {
398
+ log . verbose ( 'ninja extracting: ' , name )
399
+ }
400
+ return true
401
+ }
402
+
403
+ fs . mkdirSync ( ninjaPath )
404
+ res . pipe ( tar . extract ( {
405
+ cwd : ninjaPath ,
406
+ strip : 1 ,
407
+ filter : logger
408
+ } ) . on ( 'end' , done ) . on ( 'error' , done ) )
409
+ } )
410
+ . on ( 'error' , done )
411
+ }
412
+
413
+ function buildNinja ( done ) {
414
+ const bootstrap = spawn ( 'python' , [ 'configure.py' , '--bootstrap' ] , {
415
+ cwd : ninjaPath ,
416
+ env : process . env
417
+ } )
418
+
419
+ bootstrap . on ( 'close' , ( code ) => {
420
+ const error = 'Failed to build Ninja'
421
+ if ( code !== 0 ) done ( error )
422
+ done ( )
423
+ } )
424
+
425
+ const dataFn = ( data ) => {
426
+ log . verbose ( 'ninja bootstrap' , data . toString ( ) . trim ( ) )
427
+ }
428
+
429
+ bootstrap . stdout . on ( 'data' , dataFn )
430
+ bootstrap . stderr . on ( 'data' , dataFn )
431
+ }
432
+
355
433
/**
356
434
* Checks if a given filename is "valid" for this installation.
357
435
*/
0 commit comments