Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 6BBF5105F2 for ; Sun, 16 Feb 2014 17:01:31 +0000 (UTC) Received: (qmail 37699 invoked by uid 500); 16 Feb 2014 17:01:30 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 37641 invoked by uid 500); 16 Feb 2014 17:01:30 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 37632 invoked by uid 99); 16 Feb 2014 17:01:29 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 16 Feb 2014 17:01:29 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id A2EF082768A; Sun, 16 Feb 2014 17:01:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: agrieve@apache.org To: commits@cordova.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: git commit: CB-6049, CB-5181 Enable stdio for build sub-commands & add a spawn() helper. Date: Sun, 16 Feb 2014 17:01:29 +0000 (UTC) Repository: cordova-cli Updated Branches: refs/heads/master 8bc03dc00 -> a3c8badd8 CB-6049, CB-5181 Enable stdio for build sub-commands & add a spawn() helper. Project: http://git-wip-us.apache.org/repos/asf/cordova-cli/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-cli/commit/a3c8badd Tree: http://git-wip-us.apache.org/repos/asf/cordova-cli/tree/a3c8badd Diff: http://git-wip-us.apache.org/repos/asf/cordova-cli/diff/a3c8badd Branch: refs/heads/master Commit: a3c8badd8d066e92e5a9149e8e334c1eb8f5c066 Parents: 8bc03dc Author: Andrew Grieve Authored: Sun Feb 16 11:56:09 2014 -0500 Committer: Andrew Grieve Committed: Sun Feb 16 11:59:46 2014 -0500 ---------------------------------------------------------------------- spec/compile.spec.js | 51 +++------------------- src/compile.js | 89 +++++++++++--------------------------- src/superspawn.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 108 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a3c8badd/spec/compile.spec.js ---------------------------------------------------------------------- diff --git a/spec/compile.spec.js b/spec/compile.spec.js index 79761bf..ddccdf1 100644 --- a/spec/compile.spec.js +++ b/spec/compile.spec.js @@ -18,12 +18,12 @@ */ var cordova = require('../cordova'), platforms = require('../platforms'), - child_process = require('child_process'), path = require('path'), fs = require('fs'), hooker = require('../src/hooker'), - Q = require('q'), + superspawn = require('../src/superspawn'), util = require('../src/util'), + Q = require('q'), os = require('os'); @@ -31,38 +31,8 @@ var supported_platforms = Object.keys(platforms).filter(function(p) { return p ! describe('compile command', function() { - var is_cordova, list_platforms, fire, result, child, spawn_wrap, cd_project_root; + var is_cordova, list_platforms, fire, result, cd_project_root; var project_dir = '/some/path'; - child = { - on: function(child_event,cb){ - if(child_event === 'close'){ - cb(0); - } - }, - stdout: { - setEncoding: function(){}, - on: function(){} - }, - stderr: { - setEncoding: function(){}, - on: function(){} - } - }; - spawn_wrap = function(cmd,args,options){ - var _cmd = cmd, - _args = args; - - if (os.platform() === 'win32') { - _args = ['/c',_cmd].concat(_args); - _cmd = 'cmd'; - } - - return { - "cmd": _cmd, - "args": _args, - "options": options - }; - }; function wrapper(f, post) { runs(function() { @@ -76,7 +46,7 @@ describe('compile command', function() { cd_project_root = spyOn(util, 'cdProjectRoot').andReturn(project_dir); list_platforms = spyOn(util, 'listPlatforms').andReturn(supported_platforms); fire = spyOn(hooker.prototype, 'fire').andReturn(Q()); - spyOn(child_process, 'spawn').andReturn(child); + spyOn(superspawn, 'spawn').andCallFake(function() { return Q() }); }); describe('failure', function() { it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function() { @@ -94,24 +64,17 @@ describe('compile command', function() { }); describe('success', function() { - var spawn_call; it('should run inside a Cordova-based project with at least one added platform and shell out to build', function(done) { cordova.raw.compile(['android','ios']).then(function() { - spawn_call = spawn_wrap(path.join(project_dir, 'platforms', 'android', 'cordova', 'build'),[]); - expect(child_process.spawn).toHaveBeenCalledWith(spawn_call.cmd, spawn_call.args); - - spawn_call = spawn_wrap(path.join(project_dir, 'platforms', 'ios', 'cordova', 'build'),[]); - expect(child_process.spawn).toHaveBeenCalledWith(spawn_call.cmd, spawn_call.args); - + expect(superspawn.spawn).toHaveBeenCalledWith(path.join(project_dir, 'platforms', 'android', 'cordova', 'build'), [], jasmine.any(Object)); + expect(superspawn.spawn).toHaveBeenCalledWith(path.join(project_dir, 'platforms', 'ios', 'cordova', 'build'), [], jasmine.any(Object)); done(); }); }); it('should pass down optional parameters', function (done) { cordova.raw.compile({platforms:["blackberry10"], options:["--release"]}).then(function () { - spawn_call = spawn_wrap(path.join(project_dir, 'platforms', 'blackberry10', 'cordova', 'build'),['--release']); - expect(child_process.spawn).toHaveBeenCalledWith(spawn_call.cmd, spawn_call.args); - + expect(superspawn.spawn).toHaveBeenCalledWith(path.join(project_dir, 'platforms', 'blackberry10', 'cordova', 'build'), ['--release'], jasmine.any(Object)); done(); }); }); http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a3c8badd/src/compile.js ---------------------------------------------------------------------- diff --git a/src/compile.js b/src/compile.js index 0daf56a..02f81b3 100644 --- a/src/compile.js +++ b/src/compile.js @@ -20,78 +20,39 @@ /*global require: true, module: true, process: true*/ /*jslint sloppy: true, white: true, newcap: true */ -var cordova_util = require('./util'), - path = require('path'), - child_process = require('child_process'), +var path = require('path'), + cordova_util = require('./util'), hooker = require('./hooker'), - events = require('./events'), - Q = require('q'), - os = require('os'); - -// Returns a promise. -function shell_out_to_build(projectRoot, platform, options) { - var cmd = path.join(projectRoot, 'platforms', platform, 'cordova', 'build'), - args = options.length ? options : [], - d = Q.defer(), - errors = "", - child; - - if (os.platform() === 'win32') { - args = ['/c',cmd].concat(args); - cmd = 'cmd'; - } - - events.emit('log', 'Compiling app on platform "' + platform + '" via command "' + cmd + '" ' + args.join(" ")); - - //using spawn instead of exec to avoid errors with stdout on maxBuffer - child = child_process.spawn(cmd, args); - child.stdout.setEncoding('utf8'); - child.stdout.on('data', function (data) { - events.emit('verbose', data); - }); - - child.stderr.setEncoding('utf8'); - child.stderr.on('data', function (data) { - events.emit('verbose', data); - errors = errors + data; - }); - - child.on('close', function (code) { - events.emit('verbose', "child_process.spawn(" + cmd + "," + "[" + args.join(", ") + "]) = " + code); - if (code === 0) { - events.emit('log', 'Platform "' + platform + '" compiled successfully.'); - d.resolve(); - } else { - d.reject(new Error('An error occurred while building the ' + platform + ' project.' + errors)); - } - }); - - return d.promise; -} + superspawn = require('./superspawn'), + events = require('./events'); // Returns a promise. module.exports = function compile(options) { - var projectRoot = cordova_util.cdProjectRoot(), - hooks; + var projectRoot = cordova_util.cdProjectRoot(); - if (!options) { - options = { - verbose: false, - platforms: [], - options: [] - }; - } + options = options || { + verbose: false, + platforms: [], + options: [] + }; options = cordova_util.preProcessOptions(options); - hooks = new hooker(projectRoot); - return hooks.fire('before_compile', options) - .then(function() { - // Iterate over each added platform - return Q.all(options.platforms.map(function(platform) { - return shell_out_to_build(projectRoot, platform, options.options); - })); - }).then(function() { + var hooks = new hooker(projectRoot); + var ret = hooks.fire('before_compile', options); + options.platforms.forEach(function(platform) { + ret = ret.then(function() { + var cmd = path.join(projectRoot, 'platforms', platform, 'cordova', 'build'); + + events.emit('log', 'Compiling ' + platform + ' project'); + return superspawn.spawn(cmd, options.options, { stdio: 'inherit' }) + .then(function() { + events.emit('log', 'Platform "' + platform + '" compiled successfully.'); + }); + }); + }); + ret = ret.then(function() { return hooks.fire('after_compile', options); }); + return ret; }; http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/a3c8badd/src/superspawn.js ---------------------------------------------------------------------- diff --git a/src/superspawn.js b/src/superspawn.js new file mode 100644 index 0000000..c827ec3 --- /dev/null +++ b/src/superspawn.js @@ -0,0 +1,107 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var child_process = require('child_process'); +var fs = require('fs'); +var _ = require('underscore'); +var Q = require('q'); +var shell = require('shelljs'); +var events = require('./events'); + +// On Windows, spawn() for batch files requires absolute path & having the extension. +function resolvePath(cmd) { + if (fs.exists(cmd)) { + return cmd; + } + cmd = shell.which(cmd) || cmd; + if (!fs.exists(cmd)) { + ['.cmd', '.bat', '.js'].some(function(ext) { + if (fs.exists(cmd + ext)) { + cmd = cmd + ext; + return true; + } + }); + } + return cmd; +} + +// opts: +// printCommand: Whether to log the command (default: false) +// stdio: 'default' is to capture output and returning it as a string to success (same as exec) +// 'ignore' means don't bother capturing it +// 'inherit' means pipe the input & output. This is required for anything that prompts. +// env: Map of extra environment variables. +// cwd: Working directory for the command. +// Returns a promise that succeeds only for return code = 0. +exports.spawn = function(cmd, args, opts) { + args = args || []; + var d = Q.defer(); + if (process.platform.slice(0, 3) == 'win') { + cmd = resolvePath(cmd); + // If we couldn't find the file, likely we'll end up failing, + // but for things like "del", cmd will do the trick. + if (!fs.exists(cmd)) { + args = ['/c', cmd].concat(args); + cmd = 'cmd'; + } + } + + events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + cmd + ' Args: ' + args); + + var spawnOpts = {}; + if (opts.stdio == 'ignore') { + spawnOpts.stdio = 'ignore'; + } else if (opts.stdio == 'inherit') { + spawnOpts.stdio = 'inherit'; + } + if (opts.cwd) { + spawnOpts.cwd = opts.cwd; + } + if (opts.env) { + spawnOpts.env = _.extend(_.extend({}, process.env), opts.env); + } + + var child = child_process.spawn(cmd, args, spawnOpts); + var capturedOut = ''; + var capturedErr = ''; + + if (child.stdout) { + child.stdout.setEncoding('utf8'); + child.stdout.on('data', function(data) { + capturedOut += data; + }); + + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function(data) { + capturedErr += data; + }); + } + + child.on('close', function(code) { + events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args); + if (code === 0) { + d.resolve(capturedOut.trim()); + } else { + d.reject(new Error(capturedErr.trim())); + } + }); + + return d.promise; +}; +