Skip to content

Commit 6d5ee8f

Browse files
committed
feat: support gyp with ninja
1 parent aaf33c3 commit 6d5ee8f

File tree

5 files changed

+142
-40
lines changed

5 files changed

+142
-40
lines changed

addon.gypi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@
169169
# clients of class 'node::ObjectWrap'
170170
4251
171171
],
172+
'configurations': {
173+
'Debug_x64': {
174+
'inherit_from': ['Debug'],
175+
},
176+
'Release_x64': {
177+
'inherit_from': ['Release'],
178+
}
179+
}
172180
}, {
173181
# OS!="win"
174182
'defines': [

lib/build.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@ function build (gyp, argv, callback) {
1919
})
2020
}
2121

22-
var makeCommand = gyp.opts.make || process.env.MAKE || platformMake
23-
var command = win ? 'msbuild' : makeCommand
2422
var jobs = gyp.opts.jobs || process.env.JOBS
2523
var buildType
2624
var config
2725
var arch
2826
var nodeDir
2927
var guessedSolution
3028

29+
const makeCommand = gyp.opts.make || process.env.MAKE || platformMake
30+
let command = win ? 'msbuild' : makeCommand
31+
if (gyp.opts.ninja) {
32+
const ninjaPath = gyp.opts['ninja-path'] || path.resolve(gyp.devDir, 'ninja')
33+
command = path.resolve(ninjaPath, 'ninja')
34+
}
35+
3136
loadConfigGypi()
3237

3338
/**
@@ -127,8 +132,12 @@ function build (gyp, argv, callback) {
127132
var verbose = log.levels[log.level] <= log.levels.verbose
128133
var j
129134

130-
if (!win && verbose) {
131-
argv.push('V=1')
135+
if (verbose) {
136+
if (gyp.opts.ninja) {
137+
argv.push('-v')
138+
} else if (!win) {
139+
argv.push('V=1')
140+
}
132141
}
133142

134143
if (win && !verbose) {
@@ -141,7 +150,10 @@ function build (gyp, argv, callback) {
141150
}
142151

143152
// Specify the build type, Release by default
144-
if (win) {
153+
if (gyp.opts.ninja) {
154+
argv.push('-C')
155+
argv.push(path.join('build', buildType))
156+
} else if (win) {
145157
// Convert .gypi config target_arch to MSBuild /Platform
146158
// Since there are many ways to state '32-bit Intel', default to it.
147159
// N.B. msbuild's Condition string equality tests are case-insensitive.

lib/configure.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ function configure (gyp, argv, callback) {
225225
return callback(err)
226226
}
227227

228-
if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
228+
if (!gyp.opts.ninja) {
229+
argv.push('-f', 'ninja')
230+
} else if (!~argv.indexOf('-f') && !~argv.indexOf('--format')) {
229231
if (win) {
230232
log.verbose('gyp', 'gyp format was not specified; forcing "msvs"')
231233
// force the 'make' target for non-Windows

lib/install.js

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ const request = require('request')
1111
const processRelease = require('./process-release')
1212
const win = process.platform === 'win32'
1313
const getProxyFromURI = require('./proxy')
14+
const { spawn } = require('child_process')
1415

1516
function install (fs, gyp, argv, callback) {
1617
var release = processRelease(argv, gyp, process.version, process.release)
1718

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+
1822
// ensure no double-callbacks happen
1923
function cb (err) {
2024
if (cb.done) {
@@ -62,42 +66,62 @@ function install (fs, gyp, argv, callback) {
6266
// the directory where the dev files will be installed
6367
var devDir = path.resolve(gyp.devDir, release.versionDir)
6468

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
9687
}
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+
})
97105
})
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+
}
98122
})
99123
} else {
100-
go()
124+
handleInstall()
101125
}
102126

103127
function getContentSha (res, callback) {
@@ -297,7 +321,7 @@ function install (fs, gyp, argv, callback) {
297321
}
298322

299323
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`...')
301325
var archs = ['ia32', 'x64', 'arm64']
302326
var async = archs.length
303327
archs.forEach(function (arch) {
@@ -352,6 +376,60 @@ function install (fs, gyp, argv, callback) {
352376
}) // mkdir()
353377
} // go()
354378

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+
355433
/**
356434
* Checks if a given filename is "valid" for this installation.
357435
*/

lib/node-gyp.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ proto.configDefs = {
7575
'dist-url': String, // 'install'
7676
tarball: String, // 'install'
7777
jobs: String, // 'build'
78-
thin: String // 'configure'
78+
thin: String, // 'configure'
79+
ninja: String, // 'everywhere
80+
'ninja-path': String
7981
}
8082

8183
/**

0 commit comments

Comments
 (0)