diff --git a/bin/node-gyp.js b/bin/node-gyp.js index 70d7d50262..d5eb75c911 100755 --- a/bin/node-gyp.js +++ b/bin/node-gyp.js @@ -22,6 +22,7 @@ var path = require('path') var prog = gyp() var completed = false prog.parseArgv(process.argv) +prog.buildDir = prog.opts['build-dir'] || 'build' prog.devDir = prog.opts.devdir var homeDir = osenv.home() diff --git a/lib/build.js b/lib/build.js index 22f2583694..3301f724c1 100644 --- a/lib/build.js +++ b/lib/build.js @@ -29,7 +29,7 @@ function build (gyp, argv, callback) { var release = processRelease(argv, gyp, process.version, process.release) , makeCommand = gyp.opts.make || process.env.MAKE || platformMake , command = win ? 'msbuild' : makeCommand - , buildDir = path.resolve('build') + , buildDir = path.resolve(gyp.buildDir) , configPath = path.resolve(buildDir, 'config.gypi') , jobs = gyp.opts.jobs || process.env.JOBS , buildType @@ -86,7 +86,7 @@ function build (gyp, argv, callback) { */ function findSolutionFile () { - glob('build/*.sln', function (err, files) { + glob(buildDir + '/*.sln', function (err, files) { if (err) return callback(err) if (files.length === 0) { return callback(new Error('Could not find *.sln file. Did you run "configure"?')) @@ -240,7 +240,7 @@ function build (gyp, argv, callback) { argv.push('BUILDTYPE=' + buildType) // Invoke the Makefile in the 'build' dir. argv.push('-C') - argv.push('build') + argv.push(buildDir) if (jobs) { var j = parseInt(jobs, 10) if (!isNaN(j) && j > 0) { diff --git a/lib/clean.js b/lib/clean.js index e69164d45a..d8ac998d53 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -14,7 +14,7 @@ var log = require('npmlog') function clean (gyp, argv, callback) { // Remove the 'build' dir - var buildDir = 'build' + var buildDir = gyp.buildDir log.verbose('clean', 'removing "%s" directory', buildDir) rm(buildDir, callback) diff --git a/lib/configure.js b/lib/configure.js index 54bb2ccb3a..2a05d0a934 100644 --- a/lib/configure.js +++ b/lib/configure.js @@ -28,7 +28,7 @@ exports.usage = 'Generates ' + (win ? 'MSVC project files' : 'a Makefile') + ' f function configure (gyp, argv, callback) { var python = gyp.opts.python || process.env.PYTHON || 'python2' - , buildDir = path.resolve('build') + , buildDir = path.resolve(gyp.buildDir) , configNames = [ 'config.gypi', 'common.gypi' ] , configs = [] , nodeDir @@ -154,12 +154,13 @@ function configure (gyp, argv, callback) { return v } - log.silly('build/' + configFilename, config) + log.silly(gyp.buildDir + '/' + configFilename, config) // now write out the config.gypi file to the build/ dir var prefix = '# Do not edit. File was generated by node-gyp\'s "configure" step' , json = JSON.stringify(config, boolsToString, 2) - log.verbose('build/' + configFilename, 'writing out config file: %s', configPath) + log.verbose(gyp.buildDir + '/' + configFilename, + 'writing out config file: %s', configPath) configs.push(configPath) fs.writeFile(configPath, [prefix, json, ''].join('\n'), findConfigs) } @@ -254,7 +255,7 @@ function configure (gyp, argv, callback) { if (err) common_gypi = path.resolve(nodeDir, 'common.gypi') - var output_dir = 'build' + var output_dir = gyp.buildDir if (win) { // Windows expects an absolute path output_dir = buildDir diff --git a/lib/node-gyp.js b/lib/node-gyp.js index a841161e32..6dcf84a4e6 100644 --- a/lib/node-gyp.js +++ b/lib/node-gyp.js @@ -48,6 +48,7 @@ function Gyp () { this.devDir = '' this.commands = {} + this.buildDir = '' commands.forEach(function (command) { self.commands[command] = function (argv, callback) { @@ -89,6 +90,7 @@ proto.configDefs = { , 'tarball': String // 'install' , jobs: String // 'build' , thin: String // 'configure' + , 'build-dir': String // everywhere } /** diff --git a/test/test-addon.js b/test/test-addon.js index c2a71f4498..544d1ad2ad 100644 --- a/test/test-addon.js +++ b/test/test-addon.js @@ -1,10 +1,26 @@ 'use strict' var test = require('tape') -var execFile = require('child_process').execFile +var childProcess = require('child_process') +var execFile = childProcess.execFile +var exec = childProcess.exec var path = require('path') var addonPath = path.resolve(__dirname, 'node_modules', 'hello_world') var nodeGyp = path.resolve(__dirname, '..', 'bin', 'node-gyp.js') +var rimraf = require('rimraf') + +function cleanup (dir) { + return function teardown (t) { + Object.keys(require.cache) + .forEach(function (d) { + if (d.indexOf(addonPath) == 0) + delete require.cache[d] + }) + rimraf(dir, t.end) + } +} + +test('build simple addon - setup', cleanup(path.join(addonPath, 'build'))) test('build simple addon', function (t) { t.plan(3) @@ -26,3 +42,35 @@ test('build simple addon', function (t) { proc.stdout.setEncoding('utf-8') proc.stderr.setEncoding('utf-8') }) + +test('build simple addon - teardown', cleanup(path.join(addonPath, 'build'))) + +test('build with different build dir - setup', + cleanup(path.join(addonPath, 'foo'))) + +test('build with different build dir', function(t) { + var cmd = [nodeGyp, 'rebuild', '-C', + addonPath, '--loglevel=verbose', '--build-dir=foo'] + var proc = execFile(process.execPath, cmd, function (err, stdout, stderr) { + var logLines = stderr.toString().trim().split(/\r?\n/) + var lastLine = logLines[logLines.length-1] + t.strictEqual(err, null) + t.strictEqual(lastLine, 'gyp info ok', 'should end in ok') + + try { + var binding = require(path.join(addonPath, 'foo/Release/hello.node')) + t.strictEqual(binding.hello(), 'world') + + // Cleanup + execFile(process.execPath, [nodeGyp, '--build-dir=foo', 'clean'], t.end) + } catch (error) { + t.error(error, 'load module') + } + }) + + proc.stdout.setEncoding('utf-8') + proc.stderr.setEncoding('utf-8') +}) + +test('build with different build dir - teardown', + cleanup(path.join(addonPath, 'foo')))