You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2016/03/19 01:56:54 UTC

[07/37] cordova-lib git commit: CB-10519 Wrap all sync calls inside of `cordova.raw` methods into promises

CB-10519 Wrap all sync calls inside of `cordova.raw` methods into promises


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/4121b11f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/4121b11f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/4121b11f

Branch: refs/heads/common-1.1.x
Commit: 4121b11f050d8421ccd42a55ed21a0474039439a
Parents: b04db5f
Author: Vladimir Kotikov <v-...@microsoft.com>
Authored: Wed Feb 10 15:45:19 2016 +0300
Committer: Vladimir Kotikov <v-...@microsoft.com>
Committed: Fri Feb 19 14:06:11 2016 +0300

----------------------------------------------------------------------
 cordova-lib/spec-cordova/build.spec.js   |   6 +-
 cordova-lib/spec-cordova/compile.spec.js |  30 +-
 cordova-lib/spec-cordova/emulate.spec.js |  33 +-
 cordova-lib/spec-cordova/prepare.spec.js |   4 +-
 cordova-lib/src/cordova/build.js         |  29 +-
 cordova-lib/src/cordova/clean.js         |  31 +-
 cordova-lib/src/cordova/compile.js       |  89 ++---
 cordova-lib/src/cordova/emulate.js       |  38 +-
 cordova-lib/src/cordova/platform.js      | 121 +++----
 cordova-lib/src/cordova/plugin.js        | 480 +++++++++++++-------------
 cordova-lib/src/cordova/prepare.js       |  56 +--
 cordova-lib/src/cordova/requirements.js  |  30 +-
 cordova-lib/src/cordova/run.js           |  99 +++---
 cordova-lib/src/cordova/serve.js         |  41 +--
 14 files changed, 567 insertions(+), 520 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/spec-cordova/build.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/build.spec.js b/cordova-lib/spec-cordova/build.spec.js
index f5f1b2b..66f0869 100644
--- a/cordova-lib/spec-cordova/build.spec.js
+++ b/cordova-lib/spec-cordova/build.spec.js
@@ -40,7 +40,8 @@ describe('build command', function() {
     describe('failure', function() {
         it('should not run inside a project with no platforms', function(done) {
             list_platforms.andReturn([]);
-            Q().then(cordova.raw.build).then(function() {
+            cordova.raw.build()
+            .then(function() {
                 expect('this call').toBe('fail');
             }, function(err) {
                 expect(err.message).toEqual(
@@ -52,7 +53,8 @@ describe('build command', function() {
         it('should not run outside of a Cordova-based project', function(done) {
             is_cordova.andReturn(false);
 
-            Q().then(cordova.raw.build).then(function() {
+            cordova.raw.build()
+            .then(function() {
                 expect('this call').toBe('fail');
             }, function(err) {
                 expect(err.message).toEqual(

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/spec-cordova/compile.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/compile.spec.js b/cordova-lib/spec-cordova/compile.spec.js
index 7f4e8f6..9f4040f 100644
--- a/cordova-lib/spec-cordova/compile.spec.js
+++ b/cordova-lib/spec-cordova/compile.spec.js
@@ -26,16 +26,9 @@ var supported_platforms = Object.keys(platforms).filter(function(p) { return p !
 
 
 describe('compile command', function() {
-    var is_cordova, list_platforms, fire, result, cd_project_root, fail, platformApi, getPlatformApi;
+    var is_cordova, list_platforms, fire, cd_project_root, fail, platformApi, getPlatformApi;
     var project_dir = '/some/path';
 
-    function wrapper(f, post) {
-        runs(function() {
-            Q().then(f).then(function() { result = true; }, function(err) { result = err; });
-        });
-        waitsFor(function() { return result; }, 'promise never resolved', 500);
-        runs(post);
-    }
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
         cd_project_root = spyOn(util, 'cdProjectRoot').andReturn(project_dir);
@@ -46,16 +39,29 @@ describe('compile command', function() {
         fail = function (err) { expect(err.stack).not.toBeDefined(); };
     });
     describe('failure', function() {
-        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function() {
+        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function(done) {
             list_platforms.andReturn([]);
-            wrapper(cordova.raw.compile, function() {
+            var success = jasmine.createSpy('success');
+            cordova.raw.compile()
+            .then(success, function(result) {
+                expect(result instanceof Error).toBe(true);
                 expect('' + result).toContain('No platforms added to this project. Please use `cordova platform add <platform>`.');
+            })
+            .fin(function() {
+                expect(success).not.toHaveBeenCalled();
+                done();
             });
         });
-        it('should not run outside of a Cordova-based project', function() {
+        it('should not run outside of a Cordova-based project', function(done) {
             is_cordova.andReturn(false);
-            wrapper(cordova.raw.compile, function() {
+            var success = jasmine.createSpy('success');
+            cordova.raw.compile()
+            .then(success, function(result) {
                 expect(result instanceof Error).toBe(true);
+            })
+            .fin(function() {
+                expect(success).not.toHaveBeenCalled();
+                done();
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/spec-cordova/emulate.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/emulate.spec.js b/cordova-lib/spec-cordova/emulate.spec.js
index 5cea968..3f9b093 100644
--- a/cordova-lib/spec-cordova/emulate.spec.js
+++ b/cordova-lib/spec-cordova/emulate.spec.js
@@ -25,18 +25,10 @@ var cordova = require('../src/cordova/cordova'),
 var supported_platforms = Object.keys(platforms).filter(function(p) { return p != 'www'; });
 
 describe('emulate command', function() {
-    var is_cordova, cd_project_root, list_platforms, fire, result, fail;
+    var is_cordova, cd_project_root, list_platforms, fire, fail;
     var project_dir = '/some/path';
     var prepare_spy, platformApi, getPlatformApi;
 
-    function wrapper(f, post) {
-        runs(function() {
-            Q().then(f).then(function() { result = true; }, function(err) { result = err; });
-        });
-        waitsFor(function() { return result; }, 'promise never resolved', 500);
-        runs(post);
-    }
-
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
         cd_project_root = spyOn(util, 'cdProjectRoot').andReturn(project_dir);
@@ -48,16 +40,29 @@ describe('emulate command', function() {
         getPlatformApi = spyOn(platforms, 'getPlatformApi').andReturn(platformApi);
     });
     describe('failure', function() {
-        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function() {
+        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function(done) {
             list_platforms.andReturn([]);
-            wrapper(cordova.raw.emulate, function() {
-                expect(''+ result).toContain('No platforms added to this project. Please use `cordova platform add <platform>`.');
+            var success = jasmine.createSpy('success');
+            cordova.raw.compile()
+            .then(success, function(result) {
+                expect(result instanceof Error).toBe(true);
+                expect('' + result).toContain('No platforms added to this project. Please use `cordova platform add <platform>`.');
+            })
+            .fin(function() {
+                expect(success).not.toHaveBeenCalled();
+                done();
             });
         });
-        it('should not run outside of a Cordova-based project', function() {
+        it('should not run outside of a Cordova-based project', function(done) {
             is_cordova.andReturn(false);
-            wrapper(cordova.raw.emulate, function() {
+            var success = jasmine.createSpy('success');
+            cordova.raw.compile()
+            .then(success, function(result) {
                 expect(result instanceof Error).toBe(true);
+            })
+            .fin(function() {
+                expect(success).not.toHaveBeenCalled();
+                done();
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/spec-cordova/prepare.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/prepare.spec.js b/cordova-lib/spec-cordova/prepare.spec.js
index e475dce..f01634f 100644
--- a/cordova-lib/spec-cordova/prepare.spec.js
+++ b/cordova-lib/spec-cordova/prepare.spec.js
@@ -95,7 +95,7 @@ describe('prepare command', function() {
         it('should not run outside of a cordova-based project by calling util.isCordova', function(done) {
             is_cordova.andReturn(false);
             cd_project_root.andCallThrough();  // undo spy here because prepare depends on cdprojectRoot for isCordova check
-            Q().then(prepare).then(function() {
+            prepare().then(function() {
                 expect('this call').toBe('fail');
             }, function(err) {
                 expect('' + err).toContain('Current working directory is not a Cordova-based project.');
@@ -103,7 +103,7 @@ describe('prepare command', function() {
         });
         it('should not run inside a cordova-based project with no platforms', function(done) {
             list_platforms.andReturn([]);
-            Q().then(prepare).then(function() {
+            prepare().then(function() {
                 expect('this call').toBe('fail');
             }, function(err) {
                 expect('' + err).toContain('No platforms added to this project. Please use `cordova platform add <platform>`.');

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/build.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/build.js b/cordova-lib/src/cordova/build.js
index d734d1a..fa3dca2 100644
--- a/cordova-lib/src/cordova/build.js
+++ b/cordova-lib/src/cordova/build.js
@@ -17,22 +17,25 @@
     under the License.
 */
 
-var cordovaUtil      = require('./util'),
-    HooksRunner           = require('../hooks/HooksRunner');
+var Q = require('q'),
+    cordovaUtil = require('./util'),
+    HooksRunner = require('../hooks/HooksRunner');
 
 // Returns a promise.
 module.exports = function build(options) {
-    var projectRoot = cordovaUtil.cdProjectRoot();
-    options = cordovaUtil.preProcessOptions(options);
+    return Q().then(function() {
+        var projectRoot = cordovaUtil.cdProjectRoot();
+        options = cordovaUtil.preProcessOptions(options);
 
-    // fire build hooks
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_build', options)
-    .then(function() {
-        return require('./cordova').raw.prepare(options);
-    }).then(function() {
-        return require('./cordova').raw.compile(options);
-    }).then(function() {
-        return hooksRunner.fire('after_build', options);
+        // fire build hooks
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_build', options)
+        .then(function() {
+            return require('./cordova').raw.prepare(options);
+        }).then(function() {
+            return require('./cordova').raw.compile(options);
+        }).then(function() {
+            return hooksRunner.fire('after_build', options);
+        });
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/clean.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/clean.js b/cordova-lib/src/cordova/clean.js
index b91b941..4ed8962 100644
--- a/cordova-lib/src/cordova/clean.js
+++ b/cordova-lib/src/cordova/clean.js
@@ -17,7 +17,8 @@
     under the License.
 */
 
-var cordova_util = require('./util'),
+var Q = require('q'),
+    cordova_util = require('./util'),
     HooksRunner  = require('../hooks/HooksRunner'),
     events       = require('cordova-common').events,
     chain        = require('../util/promise-util').Q_chainmap,
@@ -25,20 +26,22 @@ var cordova_util = require('./util'),
 
 // Returns a promise.
 module.exports = function clean(options) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    options = cordova_util.preProcessOptions(options);
+    return Q().then(function() {
+        var projectRoot = cordova_util.cdProjectRoot();
+        options = cordova_util.preProcessOptions(options);
 
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_clean', options)
-    .then(function () {
-        return chain(options.platforms, function (platform) {
-            events.emit('verbose', 'Running cleanup for ' + platform + ' platform.');
-            return platform_lib
-                .getPlatformApi(platform)
-                .clean();
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_clean', options)
+        .then(function () {
+            return chain(options.platforms, function (platform) {
+                events.emit('verbose', 'Running cleanup for ' + platform + ' platform.');
+                return platform_lib
+                    .getPlatformApi(platform)
+                    .clean();
+            });
+        })
+        .then(function() {
+            return hooksRunner.fire('after_clean', options);
         });
-    })
-    .then(function() {
-        return hooksRunner.fire('after_clean', options);
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/compile.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/compile.js b/cordova-lib/src/cordova/compile.js
index 5834925..e540c14 100644
--- a/cordova-lib/src/cordova/compile.js
+++ b/cordova-lib/src/cordova/compile.js
@@ -1,41 +1,48 @@
-/**
-    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 cordova_util = require('./util'),
-    HooksRunner  = require('../hooks/HooksRunner'),
-    promiseUtil  = require('../util/promise-util'),
-    platform_lib = require('../platforms/platforms');
-
-// Returns a promise.
-module.exports = function compile(options) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    options = cordova_util.preProcessOptions(options);
-
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_compile', options)
-    .then(function () {
-        return promiseUtil.Q_chainmap(options.platforms, function (platform) {
-            return platform_lib
-                .getPlatformApi(platform)
-                .build(options.options);
-        });
-    }).then(function() {
-        return hooksRunner.fire('after_compile', options);
-    });
-};
+/**
+    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 cordova_util = require('./util'),
+    HooksRunner  = require('../hooks/HooksRunner'),
+    promiseUtil  = require('../util/promise-util'),
+    platform_lib = require('../platforms/platforms');
+
+// Returns a promise.
+module.exports = function compile(options) {
+    return Q().then(function() {
+        var projectRoot = cordova_util.cdProjectRoot();
+        options = cordova_util.preProcessOptions(options);
+
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_compile', options)
+        .then(function () {
+            return promiseUtil.Q_chainmap(options.platforms, function (platform) {
+                return platform_lib
+                    .getPlatformApi(platform)
+                    .build(options.options);
+            });
+        }).then(function() {
+            return hooksRunner.fire('after_compile', options);
+        }, function(error) {
+            events.emit('log', 'ERROR building one of the platforms: ' + error + '\nYou may not have the required environment or OS to build this project');
+            return Q.reject(error);
+        });
+    }).then(function() {
+        return hooksRunner.fire('after_compile', options);
+    });
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/emulate.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/emulate.js b/cordova-lib/src/cordova/emulate.js
index 62810fc..b2ac92b 100644
--- a/cordova-lib/src/cordova/emulate.js
+++ b/cordova-lib/src/cordova/emulate.js
@@ -24,24 +24,26 @@ var cordova_util = require('./util'),
 
 // Returns a promise.
 module.exports = function emulate(options) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    options = cordova_util.preProcessOptions(options);
-    options.options.device = false;
-    options.options.emulator = true;
+    return Q().then(function() {
+        var projectRoot = cordova_util.cdProjectRoot();
+        options = cordova_util.preProcessOptions(options);
+        options.options.device = false;
+        options.options.emulator = true;
 
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_emulate', options)
-    .then(function() {
-        // Run a prepare first!
-        return require('./cordova').raw.prepare(options);
-    }).then(function() {
-        // Deploy in parallel (output gets intermixed though...)
-        return Q.all(options.platforms.map(function(platform) {
-            return platform_lib
-                .getPlatformApi(platform)
-                .run(options.options);
-        }));
-    }).then(function() {
-        return hooksRunner.fire('after_emulate', options);
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_emulate', options)
+        .then(function() {
+            // Run a prepare first!
+            return require('./cordova').raw.prepare(options);
+        }).then(function() {
+            // Deploy in parallel (output gets intermixed though...)
+            return Q.all(options.platforms.map(function(platform) {
+                return platform_lib
+                    .getPlatformApi(platform)
+                    .run(options.options);
+            }));
+        }).then(function() {
+            return hooksRunner.fire('after_emulate', options);
+        });
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index 96c5f2f..2dff594 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -541,66 +541,69 @@ function addDeprecatedInformationToPlatforms(platformsList){
 // Returns a promise.
 module.exports = platform;
 function platform(command, targets, opts) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    var msg;
-    var hooksRunner = new HooksRunner(projectRoot);
-
-    if (arguments.length === 0) command = 'ls';
-
-    // Verify that targets look like platforms. Examples:
-    // - android
-    // - android@3.5.0
-    // - ../path/to/dir/with/platform/files
-    // - https://github.com/apache/cordova-android.git
-    if (targets) {
-        if (!(targets instanceof Array)) targets = [targets];
-        targets.forEach(function (t) {
-            // Trim the @version part if it's there.
-            var p = t.split('@')[0];
-            // OK if it's one of known platform names.
-            if (p in platforms) return;
-            // Not a known platform name, check if its a real path.
-            var pPath = path.resolve(t);
-            if (fs.existsSync(pPath)) return;
-
-            var msg;
-        // If target looks like a url, we will try cloning it with git
-            if (/[~:/\\.]/.test(t)) {
-                return;
-            } else {
-        // Neither path, git-url nor platform name - throw.
-                msg = 'Platform "' + t +
-                '" not recognized as a core cordova platform. See `' +
-                cordova_util.binname + ' platform list`.'
-                ;
-            }
-            throw new CordovaError(msg);
-        });
-    } else if (command == 'add' || command == 'rm') {
-        msg = 'You need to qualify `add` or `remove` with one or more platforms!';
-        return Q.reject(new CordovaError(msg));
-    }
-
+    // CB-10519 wrap function code into promise so throwing error
+    // would result in promise rejection instead of uncaught exception
+    return Q().then(function() {
+        var msg;
+        var projectRoot = cordova_util.cdProjectRoot();
+        var hooksRunner = new HooksRunner(projectRoot);
+
+        if (arguments.length === 0) command = 'ls';
+
+        // Verify that targets look like platforms. Examples:
+        // - android
+        // - android@3.5.0
+        // - ../path/to/dir/with/platform/files
+        // - https://github.com/apache/cordova-android.git
+        if (targets) {
+            if (!(targets instanceof Array)) targets = [targets];
+            targets.forEach(function (t) {
+                // Trim the @version part if it's there.
+                var p = t.split('@')[0];
+                // OK if it's one of known platform names.
+                if (p in platforms) return;
+                // Not a known platform name, check if its a real path.
+                var pPath = path.resolve(t);
+                if (fs.existsSync(pPath)) return;
+
+                var msg;
+                // If target looks like a url, we will try cloning it with git
+                if (/[~:/\\.]/.test(t)) {
+                    return;
+                } else {
+                    // Neither path, git-url nor platform name - throw.
+                    msg = 'Platform "' + t +
+                    '" not recognized as a core cordova platform. See `' +
+                    cordova_util.binname + ' platform list`.'
+                    ;
+                }
+                throw new CordovaError(msg);
+            });
+        } else if (command == 'add' || command == 'rm') {
+            msg = 'You need to qualify `add` or `remove` with one or more platforms!';
+            return Q.reject(new CordovaError(msg));
+        }
 
-    opts = opts || {};
-    opts.platforms = targets;
-
-    switch (command) {
-        case 'add':
-            return add(hooksRunner, projectRoot, targets, opts);
-        case 'rm':
-        case 'remove':
-            return remove(hooksRunner, projectRoot, targets, opts);
-        case 'update':
-        case 'up':
-            return update(hooksRunner, projectRoot, targets, opts);
-        case 'check':
-            return check(hooksRunner, projectRoot);
-        case 'save':
-            return save(hooksRunner, projectRoot, opts);
-        default:
-            return list(hooksRunner, projectRoot, opts);
-    }
+        opts = opts || {};
+        opts.platforms = targets;
+
+        switch (command) {
+            case 'add':
+                return add(hooksRunner, projectRoot, targets, opts);
+            case 'rm':
+            case 'remove':
+                return remove(hooksRunner, projectRoot, targets, opts);
+            case 'update':
+            case 'up':
+                return update(hooksRunner, projectRoot, targets, opts);
+            case 'check':
+                return check(hooksRunner, projectRoot);
+            case 'save':
+                return save(hooksRunner, projectRoot, opts);
+            default:
+                return list(hooksRunner, projectRoot, opts);
+        }
+    });
 }
 
 // Used to prevent attempts of installing platforms that are not supported on

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/plugin.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/plugin.js b/cordova-lib/src/cordova/plugin.js
index d35e7a2..35819c3 100644
--- a/cordova-lib/src/cordova/plugin.js
+++ b/cordova-lib/src/cordova/plugin.js
@@ -37,268 +37,272 @@ var cordova_util  = require('./util'),
 
 // Returns a promise.
 module.exports = function plugin(command, targets, opts) {
-    var projectRoot = cordova_util.cdProjectRoot();
-
-    // Dance with all the possible call signatures we've come up over the time. They can be:
-    // 1. plugin() -> list the plugins
-    // 2. plugin(command, Array of targets, maybe opts object)
-    // 3. plugin(command, target1, target2, target3 ... )
-    // The targets are not really targets, they can be a mixture of plugins and options to be passed to plugman.
-
-    command = command || 'ls';
-    targets = targets || [];
-    opts = opts || {};
-    if ( opts.length ) {
-        // This is the case with multiple targets as separate arguments and opts is not opts but another target.
-        targets = Array.prototype.slice.call(arguments, 1);
-        opts = {};
-    }
-    if ( !Array.isArray(targets) ) {
-        // This means we had a single target given as string.
-        targets = [targets];
-    }
-    opts.options = opts.options || [];
-    opts.plugins = [];
-
-    // TODO: Otherwise HooksRunner will be Object instead of function when run from tests - investigate why
-    var HooksRunner = require('../hooks/HooksRunner');
-    var hooksRunner = new HooksRunner(projectRoot);
-    var config_json = config.read(projectRoot);
-    var platformList = cordova_util.listPlatforms(projectRoot);
-
-    // Massage plugin name(s) / path(s)
-    var pluginPath = path.join(projectRoot, 'plugins');
-    var plugins = cordova_util.findPlugins(pluginPath);
-    if (!targets || !targets.length) {
-        if (command == 'add' || command == 'rm') {
-            return Q.reject(new CordovaError('You need to qualify `'+cordova_util.binname+' plugin add` or `'+cordova_util.binname+' plugin remove` with one or more plugins!'));
-        } else {
-            targets = [];
+    // CB-10519 wrap function code into promise so throwing error
+    // would result in promise rejection instead of uncaught exception
+    return Q().then(function () {
+        var projectRoot = cordova_util.cdProjectRoot();
+
+        // Dance with all the possible call signatures we've come up over the time. They can be:
+        // 1. plugin() -> list the plugins
+        // 2. plugin(command, Array of targets, maybe opts object)
+        // 3. plugin(command, target1, target2, target3 ... )
+        // The targets are not really targets, they can be a mixture of plugins and options to be passed to plugman.
+
+        command = command || 'ls';
+        targets = targets || [];
+        opts = opts || {};
+        if ( opts.length ) {
+            // This is the case with multiple targets as separate arguments and opts is not opts but another target.
+            targets = Array.prototype.slice.call(arguments, 1);
+            opts = {};
         }
-    }
-
-    //Split targets between plugins and options
-    //Assume everything after a token with a '-' is an option
-    var i;
-    for (i = 0; i < targets.length; i++) {
-        if (targets[i].match(/^-/)) {
-            opts.options = targets.slice(i);
-            break;
-        } else {
-            opts.plugins.push(targets[i]);
+        if ( !Array.isArray(targets) ) {
+            // This means we had a single target given as string.
+            targets = [targets];
         }
-    }
-
-    switch(command) {
-        case 'add':
-            if (!targets || !targets.length) {
-                return Q.reject(new CordovaError('No plugin specified. Please specify a plugin to add. See `'+cordova_util.binname+' plugin search`.'));
+        opts.options = opts.options || [];
+        opts.plugins = [];
+
+        // TODO: Otherwise HooksRunner will be Object instead of function when run from tests - investigate why
+        var HooksRunner = require('../hooks/HooksRunner');
+        var hooksRunner = new HooksRunner(projectRoot);
+        var config_json = config.read(projectRoot);
+        var platformList = cordova_util.listPlatforms(projectRoot);
+
+        // Massage plugin name(s) / path(s)
+        var pluginPath = path.join(projectRoot, 'plugins');
+        var plugins = cordova_util.findPlugins(pluginPath);
+        if (!targets || !targets.length) {
+            if (command == 'add' || command == 'rm') {
+                return Q.reject(new CordovaError('You need to qualify `'+cordova_util.binname+' plugin add` or `'+cordova_util.binname+' plugin remove` with one or more plugins!'));
+            } else {
+                targets = [];
             }
+        }
 
-            var xml = cordova_util.projectConfig(projectRoot);
-            var cfg = new ConfigParser(xml);
-            var searchPath = config_json.plugin_search_path || [];
-            if (typeof opts.searchpath == 'string') {
-                searchPath = opts.searchpath.split(path.delimiter).concat(searchPath);
-            } else if (opts.searchpath) {
-                searchPath = opts.searchpath.concat(searchPath);
-            }
-            // Blank it out to appease unit tests.
-            if (searchPath.length === 0) {
-                searchPath = undefined;
+        //Split targets between plugins and options
+        //Assume everything after a token with a '-' is an option
+        var i;
+        for (i = 0; i < targets.length; i++) {
+            if (targets[i].match(/^-/)) {
+                opts.options = targets.slice(i);
+                break;
+            } else {
+                opts.plugins.push(targets[i]);
             }
+        }
 
-            opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
-            return hooksRunner.fire('before_plugin_add', opts)
-            .then(function() {
-                var pluginInfoProvider = new PluginInfoProvider();
-                return opts.plugins.reduce(function(soFar, target) {
-                    return soFar.then(function() {
-                        if (target[target.length - 1] == path.sep) {
-                            target = target.substring(0, target.length - 1);
-                        }
+        switch(command) {
+            case 'add':
+                if (!targets || !targets.length) {
+                    return Q.reject(new CordovaError('No plugin specified. Please specify a plugin to add. See `'+cordova_util.binname+' plugin search`.'));
+                }
 
-                        var parts = target.split('@');
-                        var id = parts[0];
-                        var version = parts[1];
-
-                        // If no version is specified, retrieve the version (or source) from config.xml
-                        if (!version && !cordova_util.isUrl(id) && !cordova_util.isDirectory(id)) {
-                            events.emit('verbose', 'no version specified, retrieving version from config.xml');
-                            var ver = getVersionFromConfigFile(id, cfg);
-
-                            if (cordova_util.isUrl(ver) || cordova_util.isDirectory(ver)) {
-                                target = ver;
-                            } else {
-                                //if version exists from config.xml, use that
-                                if(ver) {
-                                    target = ver ? (id + '@' + ver) : target;
+                var xml = cordova_util.projectConfig(projectRoot);
+                var cfg = new ConfigParser(xml);
+                var searchPath = config_json.plugin_search_path || [];
+                if (typeof opts.searchpath == 'string') {
+                    searchPath = opts.searchpath.split(path.delimiter).concat(searchPath);
+                } else if (opts.searchpath) {
+                    searchPath = opts.searchpath.concat(searchPath);
+                }
+                // Blank it out to appease unit tests.
+                if (searchPath.length === 0) {
+                    searchPath = undefined;
+                }
+
+                opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
+                return hooksRunner.fire('before_plugin_add', opts)
+                .then(function() {
+                    var pluginInfoProvider = new PluginInfoProvider();
+                    return opts.plugins.reduce(function(soFar, target) {
+                        return soFar.then(function() {
+                            if (target[target.length - 1] == path.sep) {
+                                target = target.substring(0, target.length - 1);
+                            }
+
+                            var parts = target.split('@');
+                            var id = parts[0];
+                            var version = parts[1];
+
+                            // If no version is specified, retrieve the version (or source) from config.xml
+                            if (!version && !cordova_util.isUrl(id) && !cordova_util.isDirectory(id)) {
+                                events.emit('verbose', 'no version specified, retrieving version from config.xml');
+                                var ver = getVersionFromConfigFile(id, cfg);
+
+                                if (cordova_util.isUrl(ver) || cordova_util.isDirectory(ver)) {
+                                    target = ver;
                                 } else {
-                                    //fetch pinned version from cordova-lib
-                                    var pinnedVer = pkgJson.cordovaPlugins[id];
-                                    target = pinnedVer ? (id + '@' + pinnedVer) : target;
+                                    //if version exists from config.xml, use that
+                                    if(ver) {
+                                        target = ver ? (id + '@' + ver) : target;
+                                    } else {
+                                        //fetch pinned version from cordova-lib
+                                        var pinnedVer = pkgJson.cordovaPlugins[id];
+                                        target = pinnedVer ? (id + '@' + pinnedVer) : target;
+                                    }
                                 }
                             }
-                        }
-
-                        // Fetch the plugin first.
-                        events.emit('verbose', 'Calling plugman.fetch on plugin "' + target + '"');
-
-                        var fetchOptions = {
-                            searchpath: searchPath,
-                            noregistry: opts.noregistry,
-                            nohooks: opts.nohooks,
-                            link: opts.link,
-                            pluginInfoProvider: pluginInfoProvider,
-                            variables: opts.cli_variables,
-                            is_top_level: true
-                        };
-
-                        return plugman.raw.fetch(target, pluginPath, fetchOptions)
-                        .then(function (directory) {
-                            return pluginInfoProvider.get(directory);
-                        });
-                    })
-                    .then(function(pluginInfo) {
-                        // Validate top-level required variables
-                        var pluginVariables = pluginInfo.getPreferences();
-                        var missingVariables = Object.keys(pluginVariables)
-                        .filter(function (variableName) {
-                            // discard variables with default value
-                            return !(pluginVariables[variableName] || opts.cli_variables[variableName]);
-                        });
 
-                        if (missingVariables.length) {
-                            shell.rm('-rf', pluginInfo.dir);
-                            var msg = 'Variable(s) missing (use: --variable ' + missingVariables.join('=value --variable ') + '=value).';
-                            return Q.reject(new CordovaError(msg));
-                        }
+                            // Fetch the plugin first.
+                            events.emit('verbose', 'Calling plugman.fetch on plugin "' + target + '"');
 
-                        // Iterate (in serial!) over all platforms in the project and install the plugin.
-                        return chainMap(platformList, function (platform) {
-                            var platformRoot = path.join(projectRoot, 'platforms', platform),
-                            options = {
-                                cli_variables: opts.cli_variables || {},
-                                browserify: opts.browserify || false,
+                            var fetchOptions = {
                                 searchpath: searchPath,
                                 noregistry: opts.noregistry,
+                                nohooks: opts.nohooks,
                                 link: opts.link,
                                 pluginInfoProvider: pluginInfoProvider,
-                                // Set up platform to install asset files/js modules to <platform>/platform_www dir
-                                // instead of <platform>/www. This is required since on each prepare platform's www dir is changed
-                                // and files from 'platform_www' merged into 'www'. Thus we need to persist these
-                                // files platform_www directory, so they'll be applied to www on each prepare.
-                                usePlatformWww: true,
-                                nohooks: opts.nohooks
+                                variables: opts.cli_variables,
+                                is_top_level: true
                             };
 
-                            events.emit('verbose', 'Calling plugman.install on plugin "' + pluginInfo.dir + '" for platform "' + platform);
-                            return plugman.raw.install(platform, platformRoot, path.basename(pluginInfo.dir), pluginPath, options);
+                            return plugman.raw.fetch(target, pluginPath, fetchOptions)
+                            .then(function (directory) {
+                                return pluginInfoProvider.get(directory);
+                            });
                         })
-                        .thenResolve(pluginInfo);
-                    })
-                    .then(function(pluginInfo){
-                        // save to config.xml
-                        if(saveToConfigXmlOn(config_json, opts)){
-                            var src = parseSource(target, opts);
-                            var attributes = {
-                                name: pluginInfo.id,
-                                spec: src ? src : '~' + pluginInfo.version
-                            };
+                        .then(function(pluginInfo) {
+                            // Validate top-level required variables
+                            var pluginVariables = pluginInfo.getPreferences();
+                            var missingVariables = Object.keys(pluginVariables)
+                            .filter(function (variableName) {
+                                // discard variables with default value
+                                return !(pluginVariables[variableName] || opts.cli_variables[variableName]);
+                            });
+
+                            if (missingVariables.length) {
+                                shell.rm('-rf', pluginInfo.dir);
+                                var msg = 'Variable(s) missing (use: --variable ' + missingVariables.join('=value --variable ') + '=value).';
+                                return Q.reject(new CordovaError(msg));
+                            }
 
-                            cfg.removePlugin(pluginInfo.id);
-                            cfg.addPlugin(attributes, opts.cli_variables);
-                            cfg.write();
+                            // Iterate (in serial!) over all platforms in the project and install the plugin.
+                            return chainMap(platformList, function (platform) {
+                                var platformRoot = path.join(projectRoot, 'platforms', platform),
+                                options = {
+                                    cli_variables: opts.cli_variables || {},
+                                    browserify: opts.browserify || false,
+                                    searchpath: searchPath,
+                                    noregistry: opts.noregistry,
+                                    link: opts.link,
+                                    pluginInfoProvider: pluginInfoProvider,
+                                    // Set up platform to install asset files/js modules to <platform>/platform_www dir
+                                    // instead of <platform>/www. This is required since on each prepare platform's www dir is changed
+                                    // and files from 'platform_www' merged into 'www'. Thus we need to persist these
+                                    // files platform_www directory, so they'll be applied to www on each prepare.
+                                    usePlatformWww: true,
+                                    nohooks: opts.nohooks
+                                };
+
+                                events.emit('verbose', 'Calling plugman.install on plugin "' + pluginInfo.dir + '" for platform "' + platform);
+                                return plugman.raw.install(platform, platformRoot, path.basename(pluginInfo.dir), pluginPath, options);
+                            })
+                            .thenResolve(pluginInfo);
+                        })
+                        .then(function(pluginInfo){
+                            // save to config.xml
+                            if(saveToConfigXmlOn(config_json, opts)){
+                                var src = parseSource(target, opts);
+                                var attributes = {
+                                    name: pluginInfo.id,
+                                    spec: src ? src : '~' + pluginInfo.version
+                                };
+
+                                cfg.removePlugin(pluginInfo.id);
+                                cfg.addPlugin(attributes, opts.cli_variables);
+                                cfg.write();
+
+                                events.emit('results', 'Saved plugin info for "' + pluginInfo.id + '" to config.xml');
+                            }
+                        });
+                    }, Q()); // end Q.all
+                }).then(function() {
+                    // Need to require right here instead of doing this at the beginning of file
+                    // otherwise tests are failing without any real reason.
+                    return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
+                }).then(function() {
+                    opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
+                    return hooksRunner.fire('after_plugin_add', opts);
+                });
+            case 'rm':
+            case 'remove':
+                if (!targets || !targets.length) {
+                    return Q.reject(new CordovaError('No plugin specified. Please specify a plugin to remove. See `'+cordova_util.binname+' plugin list`.'));
+                }
 
-                            events.emit('results', 'Saved plugin info for "' + pluginInfo.id + '" to config.xml');
-                        }
-                    });
-                }, Q()); // end Q.all
-            }).then(function() {
-                // Need to require right here instead of doing this at the beginning of file
-                // otherwise tests are failing without any real reason.
-                return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
-            }).then(function() {
                 opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
-                return hooksRunner.fire('after_plugin_add', opts);
-            });
-        case 'rm':
-        case 'remove':
-            if (!targets || !targets.length) {
-                return Q.reject(new CordovaError('No plugin specified. Please specify a plugin to remove. See `'+cordova_util.binname+' plugin list`.'));
-            }
-
-            opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
-            return hooksRunner.fire('before_plugin_rm', opts)
-            .then(function() {
-                return opts.plugins.reduce(function(soFar, target) {
-                    var validatedPluginId = validatePluginId(target, plugins);
-                    if (!validatedPluginId) {
-                        return Q.reject(new CordovaError('Plugin "' + target + '" is not present in the project. See `' + cordova_util.binname + ' plugin list`.'));
+                return hooksRunner.fire('before_plugin_rm', opts)
+                .then(function() {
+                    return opts.plugins.reduce(function(soFar, target) {
+                        var validatedPluginId = validatePluginId(target, plugins);
+                        if (!validatedPluginId) {
+                            return Q.reject(new CordovaError('Plugin "' + target + '" is not present in the project. See `' + cordova_util.binname + ' plugin list`.'));
+                        }
+                        target = validatedPluginId;
+
+                        // Iterate over all installed platforms and uninstall.
+                        // If this is a web-only or dependency-only plugin, then
+                        // there may be nothing to do here except remove the
+                        // reference from the platform's plugin config JSON.
+                        return platformList.reduce(function(soFar, platform) {
+                            return soFar.then(function() {
+                                var platformRoot = path.join(projectRoot, 'platforms', platform);
+                                events.emit('verbose', 'Calling plugman.uninstall on plugin "' + target + '" for platform "' + platform + '"');
+                                return plugman.raw.uninstall.uninstallPlatform(platform, platformRoot, target, pluginPath);
+                            });
+                        }, Q())
+                        .then(function() {
+                            // TODO: Should only uninstallPlugin when no platforms have it.
+                            return plugman.raw.uninstall.uninstallPlugin(target, pluginPath);
+                        }).then(function(){
+                            //remove plugin from config.xml
+                            if(saveToConfigXmlOn(config_json, opts)){
+                                var configPath = cordova_util.projectConfig(projectRoot);
+                                if(fs.existsSync(configPath)){//should not happen with real life but needed for tests
+                                    var configXml = new ConfigParser(configPath);
+                                    configXml.removePlugin(target);
+                                    configXml.write();
+                                    events.emit('results', 'config.xml entry for ' +target+ ' is removed');
+                                }
+                            }
+                        })
+                        .then(function(){
+                            // Remove plugin from fetch.json
+                            events.emit('verbose', 'Removing plugin ' + target + ' from fetch.json');
+                            metadata.remove_fetch_metadata(pluginPath, target);
+                        });
+                    }, Q());
+                }).then(function () {
+                    return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
+                }).then(function() {
+                    opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
+                    return hooksRunner.fire('after_plugin_rm', opts);
+                });
+            case 'search':
+                return hooksRunner.fire('before_plugin_search', opts)
+                .then(function() {
+                    var link = 'http://cordova.apache.org/plugins/';
+                    if (opts.plugins.length > 0) {
+                        var keywords = (opts.plugins).join(' ');
+                        var query = link + '?q=' + encodeURI(keywords);
+                        opener(query);
+                    }
+                    else {
+                        opener(link);
                     }
-                    target = validatedPluginId;
 
-                    // Iterate over all installed platforms and uninstall.
-                    // If this is a web-only or dependency-only plugin, then
-                    // there may be nothing to do here except remove the
-                    // reference from the platform's plugin config JSON.
-                    return platformList.reduce(function(soFar, platform) {
-                        return soFar.then(function() {
-                            var platformRoot = path.join(projectRoot, 'platforms', platform);
-                            events.emit('verbose', 'Calling plugman.uninstall on plugin "' + target + '" for platform "' + platform + '"');
-                            return plugman.raw.uninstall.uninstallPlatform(platform, platformRoot, target, pluginPath);
-                        });
-                    }, Q())
-                    .then(function() {
-                        // TODO: Should only uninstallPlugin when no platforms have it.
-                        return plugman.raw.uninstall.uninstallPlugin(target, pluginPath);
-                    }).then(function(){
-                        //remove plugin from config.xml
-                        if(saveToConfigXmlOn(config_json, opts)){
-                            var configPath = cordova_util.projectConfig(projectRoot);
-                            if(fs.existsSync(configPath)){//should not happen with real life but needed for tests
-                                var configXml = new ConfigParser(configPath);
-                                configXml.removePlugin(target);
-                                configXml.write();
-                                events.emit('results', 'config.xml entry for ' +target+ ' is removed');
-                            }
-                        }
-                    })
-                    .then(function(){
-                        // Remove plugin from fetch.json
-                        events.emit('verbose', 'Removing plugin ' + target + ' from fetch.json');
-                        metadata.remove_fetch_metadata(pluginPath, target);
-                    });
-                }, Q());
-            }).then(function () {
-                return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
-            }).then(function() {
-                opts.cordova = { plugins: cordova_util.findPlugins(pluginPath) };
-                return hooksRunner.fire('after_plugin_rm', opts);
-            });
-        case 'search':
-            return hooksRunner.fire('before_plugin_search', opts)
-            .then(function() {
-                var link = 'http://cordova.apache.org/plugins/';
-                if (opts.plugins.length > 0) {
-                    var keywords = (opts.plugins).join(' ');
-                    var query = link + '?q=' + encodeURI(keywords);
-                    opener(query);
-                }
-                else {
-                    opener(link);
-                }
-                
-                return Q.resolve();
-            }).then(function() {
-                return hooksRunner.fire('after_plugin_search', opts);
-            });
-        case 'save':
-            // save the versions/folders/git-urls of currently installed plugins into config.xml
-            return save(projectRoot, opts);
-        default:
-            return list(projectRoot, hooksRunner);
-    }
+                    return Q.resolve();
+                }).then(function() {
+                    return hooksRunner.fire('after_plugin_search', opts);
+                });
+            case 'save':
+                // save the versions/folders/git-urls of currently installed plugins into config.xml
+                return save(projectRoot, opts);
+            default:
+                return list(projectRoot, hooksRunner);
+        }
+    });
 };
 
 function validatePluginId(pluginId, installedPlugins) {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/prepare.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/prepare.js b/cordova-lib/src/cordova/prepare.js
index 2eea9f5..194122e 100644
--- a/cordova-lib/src/cordova/prepare.js
+++ b/cordova-lib/src/cordova/prepare.js
@@ -34,34 +34,36 @@ var cordova_util      = require('./util'),
 // Returns a promise.
 exports = module.exports = prepare;
 function prepare(options) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    var config_json = config.read(projectRoot);
-    options = options || { verbose: false, platforms: [], options: {} };
-
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_prepare', options)
-    .then(function(){
-        return restore.installPlatformsFromConfigXML(options.platforms, { searchpath : options.searchpath });
-    })
-    .then(function(){
-        options = cordova_util.preProcessOptions(options);
-        var paths = options.platforms.map(function(p) {
-            var platform_path = path.join(projectRoot, 'platforms', p);
-            return platforms.getPlatformApi(p, platform_path).getPlatformInfo().locations.www;
-        });
-        options.paths = paths;
-    }).then(function() {
-        options = cordova_util.preProcessOptions(options);
-        options.searchpath = options.searchpath || config_json.plugin_search_path;
-        // Iterate over each added platform
-        return preparePlatforms(options.platforms, projectRoot, options);
-    }).then(function() {
-        options.paths = options.platforms.map(function(platform) {
-            return platforms.getPlatformApi(platform).getPlatformInfo().locations.www;
+    return Q().then(function() {
+        var projectRoot = cordova_util.cdProjectRoot();
+        var config_json = config.read(projectRoot);
+        options = options || { verbose: false, platforms: [], options: {} };
+
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_prepare', options)
+        .then(function(){
+            return restore.installPlatformsFromConfigXML(options.platforms, { searchpath : options.searchpath });
+        })
+        .then(function(){
+            options = cordova_util.preProcessOptions(options);
+            var paths = options.platforms.map(function(p) {
+                var platform_path = path.join(projectRoot, 'platforms', p);
+                return platforms.getPlatformApi(p, platform_path).getPlatformInfo().locations.www;
+            });
+            options.paths = paths;
+        }).then(function() {
+            options = cordova_util.preProcessOptions(options);
+            options.searchpath = options.searchpath || config_json.plugin_search_path;
+            // Iterate over each added platform
+            return preparePlatforms(options.platforms, projectRoot, options);
+        }).then(function() {
+            options.paths = options.platforms.map(function(platform) {
+                return platforms.getPlatformApi(platform).getPlatformInfo().locations.www;
+            });
+            return hooksRunner.fire('after_prepare', options);
+        }).then(function () {
+            return restore.installPluginsFromConfigXML(options);
         });
-        return hooksRunner.fire('after_prepare', options);
-    }).then(function () {
-        return restore.installPluginsFromConfigXML(options);
     });
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/requirements.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/requirements.js b/cordova-lib/src/cordova/requirements.js
index 3304636..0d8d543 100644
--- a/cordova-lib/src/cordova/requirements.js
+++ b/cordova-lib/src/cordova/requirements.js
@@ -32,20 +32,22 @@ var knownPlatforms = require('../platforms/platforms');
  *   requirements check results for each platform.
  */
 module.exports = function check_reqs(platforms) {
-    platforms = cordova_util.preProcessOptions(platforms).platforms;
+    return Q().then(function() {
+        var platforms = cordova_util.preProcessOptions(platforms).platforms;
 
-    return Q.allSettled(platforms.map(function (platform) {
-        return knownPlatforms.getPlatformApi(platform).requirements();
-    }))
-    .then(function (settledChecks) {
-        var res = {};
-        settledChecks.reduce(function (result, settledCheck, idx) {
-            var platformName = platforms[idx];
-            result[platformName] = settledCheck.state === 'fulfilled' ?
-                settledCheck.value :
-                new CordovaError(settledCheck.reason);
-            return result;
-        }, res);
-        return res;
+        return Q.allSettled(platforms.map(function (platform) {
+            return knownPlatforms.getPlatformApi(platform).requirements();
+        }))
+        .then(function (settledChecks) {
+            var res = {};
+            settledChecks.reduce(function (result, settledCheck, idx) {
+                var platformName = platforms[idx];
+                result[platformName] = settledCheck.state === 'fulfilled' ?
+                    settledCheck.value :
+                    new CordovaError(settledCheck.reason);
+                return result;
+            }, res);
+            return res;
+        });
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/run.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/run.js b/cordova-lib/src/cordova/run.js
index fcfc9c6..f2afeed 100644
--- a/cordova-lib/src/cordova/run.js
+++ b/cordova-lib/src/cordova/run.js
@@ -1,46 +1,53 @@
-/**
-    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 cordova_util = require('./util'),
-    HooksRunner  = require('../hooks/HooksRunner'),
-    Q            = require('q'),
-    platform_lib = require('../platforms/platforms');
-
-
-// Returns a promise.
-module.exports = function run(options) {
-    var projectRoot = cordova_util.cdProjectRoot();
-    options = cordova_util.preProcessOptions(options);
-
-    var hooksRunner = new HooksRunner(projectRoot);
-    return hooksRunner.fire('before_run', options)
-    .then(function() {
-        // Run a prepare first, then shell out to run
-        return require('./cordova').raw.prepare(options);
-    }).then(function() {
-        // Deploy in parallel (output gets intermixed though...)
-        return Q.all(options.platforms.map(function(platform) {
-            return platform_lib
-                .getPlatformApi(platform)
-                .run(options.options);
-        }));
-    }).then(function() {
-        return hooksRunner.fire('after_run', options);
-    });
-};
+/**
+    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 cordova_util = require('./util'),
+    HooksRunner  = require('../hooks/HooksRunner'),
+    Q            = require('q'),
+    platform_lib = require('../platforms/platforms');
+
+
+// Returns a promise.
+module.exports = function run(options) {
+    return Q().then(function() {
+        var projectRoot = cordova_util.cdProjectRoot();
+        options = cordova_util.preProcessOptions(options);
+
+        var hooksRunner = new HooksRunner(projectRoot);
+        return hooksRunner.fire('before_run', options)
+        .then(function() {
+            // Run a prepare first, then shell out to run
+            return require('./cordova').raw.prepare(options);
+        }).then(function() {
+            // Deploy in parallel (output gets intermixed though...)
+            return Q.all(options.platforms.map(function(platform) {
+                return platform_lib
+                    .getPlatformApi(platform)
+                    .run(options.options);
+            }));
+        }).then(function() {
+            return hooksRunner.fire('after_run', options);
+        }, function(error) {
+            events.emit('log', 'ERROR running one or more of the platforms: ' + error + '\nYou may not have the required environment or OS to run this project');
+
+            // CB-10567 bubble up `run` error, so the caller still could get rejected promise
+            return Q.reject(error);
+        });
+    });
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4121b11f/cordova-lib/src/cordova/serve.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/serve.js b/cordova-lib/src/cordova/serve.js
index 59eb3e9..e24e990 100644
--- a/cordova-lib/src/cordova/serve.js
+++ b/cordova-lib/src/cordova/serve.js
@@ -112,29 +112,30 @@ function calculateMd5(fileName) {
 }
 
 module.exports = function server(port, opts) {
-    var d = Q.defer();
-    projectRoot = cordova_util.cdProjectRoot();
     port = +port || 8000;
 
-    var hooksRunner = new HooksRunner(projectRoot);
-    hooksRunner.fire('before_serve', opts).then(function () {
-        // Run a prepare first!
-        return require('./cordova').raw.prepare([]);
-    }).then(function () {
-        var server = serve();
-
-        installedPlatforms = cordova_util.listPlatforms(projectRoot);
-        installedPlatforms.forEach(function (platform) {
-            var locations = platforms.getPlatformApi(platform).getPlatformInfo().locations;
-            server.app.use('/' + platform + '/www', serve.static(locations.www));
-            server.app.get('/' + platform + '/*', getPlatformHandler(platform, locations.www, locations.configXml));
-        });
-        server.app.get('*', handleRoot);
+    return Q.promise(function(resolve) {
+        projectRoot = cordova_util.cdProjectRoot();
+
+        var hooksRunner = new HooksRunner(projectRoot);
+        hooksRunner.fire('before_serve', opts).then(function () {
+            // Run a prepare first!
+            return require('./cordova').raw.prepare([]);
+        }).then(function () {
+            var server = serve();
+
+            installedPlatforms = cordova_util.listPlatforms(projectRoot);
+            installedPlatforms.forEach(function (platform) {
+                var locations = platforms.getPlatformApi(platform).getPlatformInfo().locations;
+                server.app.use('/' + platform + '/www', serve.static(locations.www));
+                server.app.get('/' + platform + '/*', getPlatformHandler(platform, locations.www, locations.configXml));
+            });
+            server.app.get('*', handleRoot);
 
-        server.launchServer({port: port, events: events});
-        hooksRunner.fire('after_serve', opts).then(function () {
-            d.resolve(server.server);
+            server.launchServer({port: port, events: events});
+            hooksRunner.fire('after_serve', opts).then(function () {
+                resolve(server.server);
+            });
         });
     });
-    return d.promise;
 };


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org