You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/02/15 21:07:47 UTC

git commit: 2.4.7. Fixed CLI tools since addition of compile + prepare commands (callbacks were not nested properly). Fixed tests since addition of compile + prepare commands. Rolled back jasmine version since there are issues in 1.2.x. Removed ./bin/not

Updated Branches:
  refs/heads/master 69be67b86 -> 61d29e137


2.4.7. Fixed CLI tools since addition of compile + prepare commands (callbacks were not nested properly). Fixed tests since addition of compile + prepare commands. Rolled back jasmine version since there are issues in 1.2.x. Removed ./bin/notice and put it into bootstrap (for better windows support).


Project: http://git-wip-us.apache.org/repos/asf/cordova-cli/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-cli/commit/61d29e13
Tree: http://git-wip-us.apache.org/repos/asf/cordova-cli/tree/61d29e13
Diff: http://git-wip-us.apache.org/repos/asf/cordova-cli/diff/61d29e13

Branch: refs/heads/master
Commit: 61d29e137216b0b5ab2ff23caddbb05eecbfc653
Parents: 69be67b
Author: Fil Maj <ma...@gmail.com>
Authored: Fri Feb 15 12:07:30 2013 -0800
Committer: Fil Maj <ma...@gmail.com>
Committed: Fri Feb 15 12:07:30 2013 -0800

----------------------------------------------------------------------
 bin/notice                     |    7 --
 bootstrap.js                   |   10 ++
 cordova.js                     |   60 +++++++++----
 package.json                   |    6 +-
 spec/build.spec.js             |   74 +++-------------
 spec/compile.spec.js           |  167 +++++++++++++++++++++++++++++++++++
 spec/plugin_parser.spec.js     |    4 +-
 spec/prepare.spec.js           |  133 ++++++++++++++++++++++++++++
 src/compile.js                 |    4 +-
 src/metadata/android_parser.js |    5 +-
 src/platform.js                |    2 +-
 src/prepare.js                 |   64 +++++--------
 12 files changed, 401 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/bin/notice
----------------------------------------------------------------------
diff --git a/bin/notice b/bin/notice
deleted file mode 100755
index 478a1aa..0000000
--- a/bin/notice
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-echo "************************************************************************";
-echo "* Please NOTE: it is highly recommended to run the command:            *";
-echo "*     sudo chown -R <yourusername> /usr/local/lib/node_modules/cordova *";
-echo "* This will allow you to run this tool globally without root.          *";
-echo "************************************************************************";

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/bootstrap.js
----------------------------------------------------------------------
diff --git a/bootstrap.js b/bootstrap.js
index 14b6973..89a032b 100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@ -25,6 +25,7 @@ var util      = require('./src/util'),
     a_parser  = require('./src/metadata/android_parser'),
     b_parser  = require('./src/metadata/blackberry_parser'),
     i_parser  = require('./src/metadata/ios_parser'),
+    n         = require('ncallbacks'),
     path      = require('path'),
     fs        = require('fs'),
     shell     = require('shelljs'),
@@ -51,6 +52,14 @@ var platformsDir = path.join(cordovaDir, 'platforms');
 // kill the stupid spec shit!
 shell.rm('-rf', path.join(cordovaDir, 'www', 'spec'));
 
+var end = n(platforms.length, function() {
+    console.log("************************************************************************");
+    console.log("* Please NOTE: it is highly recommended to run the command:            *");
+    console.log("*     sudo chown -R <yourusername> /usr/local/lib/node_modules/cordova *");
+    console.log("* This will allow you to run this tool globally without root.          *");
+    console.log("************************************************************************");
+});
+
 platforms.forEach(function(platform) {
     min_reqs[platform](function(err) {
         if (err) {
@@ -88,6 +97,7 @@ platforms.forEach(function(platform) {
                         shell.chmod('+x', other_script_path);
                     });
                     console.log('SUCCESS: ' + platform + ' ready to rock!');
+                    end();
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/cordova.js
----------------------------------------------------------------------
diff --git a/cordova.js b/cordova.js
index 224406b..5840854 100755
--- a/cordova.js
+++ b/cordova.js
@@ -17,28 +17,56 @@
     under the License.
 */
 var cordova_events = require('./src/events'),
-    prepare = require('./src/prepare'),
-    compile = require('./src/compile');
+    prepare        = require('./src/prepare'),
+    platform       = require('./src/platform'),
+    hooker         = require('./src/hooker'),
+    util           = require('./src/util'),
+    path           = require('path'),
+    fs             = require('fs'),
+    compile        = require('./src/compile');
 
 module.exports = {
-    help:     require('./src/help'),
-    create:   require('./src/create'),
-    platform: require('./src/platform'),
-    platforms: require('./src/platform'),
-    prepare:    prepare,
-    compile:    compile,
-    emulate:  require('./src/emulate'),
-    plugin:   require('./src/plugin'),
+    help:      require('./src/help'),
+    create:    require('./src/create'),
+    platform:  platform,
+    platforms: platform,
+    prepare:   prepare,
+    compile:   compile,
+    emulate:   require('./src/emulate'),
+    plugin:    require('./src/plugin'),
     plugins:   require('./src/plugin'),
-    serve:    require('./src/serve'),
-    on:       function() {
+    serve:     require('./src/serve'),
+    on:        function() {
         cordova_events.on.apply(cordova_events, arguments);
     },
-    emit:     function() {
+    emit:      function() {
         cordova_events.emit.apply(cordova_events, arguments);
     },
-    build: function() {
-        prepare.apply(this, arguments);
-        compile.apply(this, arguments);
+    build:     function() {
+        var projectRoot = util.isCordova(process.cwd());
+        if (!projectRoot) {
+            throw new Error('Current working directory is not a Cordova-based project.');
+        }
+        var platforms_dir = path.join(projectRoot, 'platforms');
+        var platforms = fs.readdirSync(platforms_dir);
+        if (platforms.length === 0) {
+            throw new Error('No platforms added! `cordova platform add <platform>` to add a platform.');
+        }
+
+        // fire build hooks
+        var hooks = new hooker(projectRoot);
+        hooks.fire('before_build');
+
+        var prep_args = Array.prototype.slice.call(arguments, 0);
+        var compile_args = Array.prototype.slice.call(arguments, 0);
+
+        var self = this;
+        compile_args = compile_args.concat(function() {
+            hooks.fire('after_build');
+        });
+        prep_args = prep_args.concat(function() {
+            module.exports.compile.apply(self, compile_args);
+        });
+        module.exports.prepare.apply(this, prep_args);
     }
 };

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 399601e..fec2a0b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova",
-  "version": "2.4.6",
+  "version": "2.4.7",
   "preferGlobal": "true",
   "description": "Cordova command line interface tool",
   "main": "cordova",
@@ -9,7 +9,7 @@
   },
   "scripts": {
     "test": "./node_modules/jasmine-node/bin/jasmine-node --color spec",
-    "install": "node bootstrap.js && ./bin/notice"
+    "install": "node bootstrap.js"
   },
   "repository": {
     "type": "git",
@@ -34,7 +34,7 @@
     "prompt":"0.2.7"
   },
   "devDependencies": {
-    "jasmine-node":">=1.0.0"
+    "jasmine-node":"1.1.x"
   },
   "author": "Anis Kadri",
   "contributors": [

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/spec/build.spec.js
----------------------------------------------------------------------
diff --git a/spec/build.spec.js b/spec/build.spec.js
index 26e1749..2427a0b 100644
--- a/spec/build.spec.js
+++ b/spec/build.spec.js
@@ -52,6 +52,8 @@ describe('build command', function() {
     });
     
     it('should run inside a Cordova-based project with at least one added platform', function() {
+        // move platform project fixtures over to fake cordova into thinking platforms were added
+        // TODO: possibly add this to helper?
         shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
         shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
         this.after(function() {
@@ -62,11 +64,14 @@ describe('build command', function() {
 
         process.chdir(cordova_project);
 
-        var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
-        spyOn(android_parser.prototype, 'update_project');
+        var prepare_spy = spyOn(cordova, 'prepare');
+        var compile_spy = spyOn(cordova, 'compile');
         expect(function() {
             cordova.build();
-            expect(s).toHaveBeenCalled();
+            var prep_cb = prepare_spy.mostRecentCall.args[0];
+            prep_cb();
+            expect(prepare_spy).toHaveBeenCalled();
+            expect(compile_spy).toHaveBeenCalled();
         }).not.toThrow();
     });
     it('should not run outside of a Cordova-based project', function() {
@@ -81,72 +86,19 @@ describe('build command', function() {
             cordova.build();
         }).toThrow();
     });
-    describe('per platform', function() {
-        beforeEach(function() {
-            process.chdir(cordova_project);
-        });
-
-        afterEach(function() {
-            process.chdir(cwd);
-        });
-       
-        describe('Android', function() {
-            it('should shell out to build command on Android', function() {
-                var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
-                cordova.build('android');
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-            it('should call android_parser\'s update_project', function() {
-                spyOn(require('shelljs'), 'exec').andReturn({code:0});
-                var s = spyOn(android_parser.prototype, 'update_project');
-                cordova.build('android');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-        describe('iOS', function() {
-            it('should shell out to build command on iOS', function() {
-                var s = spyOn(require('shelljs'), 'exec');
-                var proj_spy = spyOn(ios_parser.prototype, 'update_project');
-                cordova.build('ios');
-                proj_spy.mostRecentCall.args[1]();
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
-            });
-            it('should call ios_parser\'s update_project', function() {
-                var s = spyOn(ios_parser.prototype, 'update_project');
-                cordova.build('ios');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-        describe('BlackBerry', function() {
-            it('should shell out to ant command on blackberry', function() {
-                var proj_spy = spyOn(blackberry_parser.prototype, 'update_project');
-                var s = spyOn(require('shelljs'), 'exec');
-                cordova.build('blackberry');
-                proj_spy.mostRecentCall.args[1](); // update_project fake
-                expect(s).toHaveBeenCalled();
-                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-device/);
-            });
-            it('should call blackberry_parser\'s update_project', function() {
-                var s = spyOn(blackberry_parser.prototype, 'update_project');
-                cordova.build('blackberry');
-                expect(s).toHaveBeenCalled();
-            });
-        });
-    });
 
     describe('hooks', function() {
-        var s, sh, ap;
+        var s, p, c;
         beforeEach(function() {
             s = spyOn(hooker.prototype, 'fire').andReturn(true);
+            p = spyOn(cordova, 'prepare');
+            c = spyOn(cordova, 'compile');
         });
 
         describe('when platforms are added', function() {
             beforeEach(function() {
                 shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
                 shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
-                sh = spyOn(shell, 'exec');
-                ap = spyOn(android_parser.prototype, 'update_project');
                 process.chdir(cordova_project);
             });
             afterEach(function() {
@@ -161,7 +113,8 @@ describe('build command', function() {
             });
             it('should fire after hooks through the hooker module', function() {
                 cordova.build();
-                sh.mostRecentCall.args[2](0); //fake shell call
+                p.mostRecentCall.args[0](); // prep cb
+                c.mostRecentCall.args[0](); // compile cb
                 expect(s).toHaveBeenCalledWith('after_build');
             });
         });
@@ -175,7 +128,6 @@ describe('build command', function() {
                 process.chdir(cwd);
             });
             it('should not fire the hooker', function() {
-                spyOn(shell, 'exec').andReturn({code:0});
                 expect(function() {
                     cordova.build();
                 }).toThrow();

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/spec/compile.spec.js
----------------------------------------------------------------------
diff --git a/spec/compile.spec.js b/spec/compile.spec.js
new file mode 100644
index 0000000..bc131a0
--- /dev/null
+++ b/spec/compile.spec.js
@@ -0,0 +1,167 @@
+/**
+    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 = require('../cordova'),
+    et = require('elementtree'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    config_parser = require('../src/config_parser'),
+    android_parser = require('../src/metadata/android_parser'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    blackberry_parser = require('../src/metadata/blackberry_parser'),
+    hooker = require('../src/hooker'),
+    fixtures = path.join(__dirname, 'fixtures'),
+    hooks = path.join(fixtures, 'hooks'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+describe('compile command', function() {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir);
+    });
+
+    it('should not run inside a Cordova-based project with no added platforms', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        cordova.create(tempDir);
+        process.chdir(tempDir);
+        expect(function() {
+            cordova.compile();
+        }).toThrow();
+    });
+    
+    it('should run inside a Cordova-based project with at least one added platform', function() {
+        // move platform project fixtures over to fake cordova into thinking platforms were added
+        // TODO: possibly add this to helper?
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+        this.after(function() {
+            process.chdir(cwd);
+            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+        });
+
+        process.chdir(cordova_project);
+
+        var sh_spy = spyOn(shell, 'exec');
+
+        expect(function() {
+            cordova.compile();
+            expect(sh_spy).toHaveBeenCalled();
+        }).not.toThrow();
+    });
+    it('should not run outside of a Cordova-based project', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        shell.mkdir('-p', tempDir);
+        process.chdir(tempDir);
+
+        expect(function() {
+            cordova.compile();
+        }).toThrow();
+    });
+
+    describe('hooks', function() {
+        var s;
+        beforeEach(function() {
+            s = spyOn(hooker.prototype, 'fire').andReturn(true);
+        });
+
+        describe('when platforms are added', function() {
+            beforeEach(function() {
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                process.chdir(cordova_project);
+            });
+            afterEach(function() {
+                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                process.chdir(cwd);
+            });
+
+            it('should fire before hooks through the hooker module', function() {
+                spyOn(shell, 'exec');
+                cordova.compile();
+                expect(s).toHaveBeenCalledWith('before_compile');
+            });
+            it('should fire after hooks through the hooker module', function() {
+                var sh_spy = spyOn(shell, 'exec');
+                cordova.compile();
+                sh_spy.mostRecentCall.args[2](0); // shell cb
+                expect(s).toHaveBeenCalledWith('after_compile');
+            });
+        });
+
+        describe('with no platforms added', function() {
+            beforeEach(function() {
+                cordova.create(tempDir);
+                process.chdir(tempDir);
+            });
+            afterEach(function() {
+                process.chdir(cwd);
+            });
+            it('should not fire the hooker', function() {
+                expect(function() {
+                    cordova.compile();
+                }).toThrow();
+                expect(s).not.toHaveBeenCalledWith('before_compile');
+                expect(s).not.toHaveBeenCalledWith('after_compile');
+            });
+        });
+    });
+    describe('per platform', function() {
+        beforeEach(function() {
+            process.chdir(cordova_project);
+        });
+
+        afterEach(function() {
+            process.chdir(cwd);
+        });
+       
+        describe('Android', function() {
+            it('should shell out to build command on Android', function() {
+                var s = spyOn(require('shelljs'), 'exec').andReturn({code:0});
+                cordova.compile('android');
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
+            });
+        });
+        describe('iOS', function() {
+            it('should shell out to build command on iOS', function() {
+                var s = spyOn(require('shelljs'), 'exec');
+                cordova.compile('ios');
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0].match(/\/cordova\/build/)).not.toBeNull();
+            });
+        });
+        describe('BlackBerry', function() {
+            it('should shell out to ant command on blackberry', function() {
+                var s = spyOn(require('shelljs'), 'exec');
+                cordova.compile('blackberry');
+                expect(s).toHaveBeenCalled();
+                expect(s.mostRecentCall.args[0]).toMatch(/ant -f .*build\.xml" qnx load-device/);
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/spec/plugin_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/plugin_parser.spec.js b/spec/plugin_parser.spec.js
index e16a6c9..050ee8d 100644
--- a/spec/plugin_parser.spec.js
+++ b/spec/plugin_parser.spec.js
@@ -35,10 +35,8 @@ describe('plugin.xml parser', function () {
     });
     it('should be able to figure out which platforms the plugin supports', function() {
         var cfg = new plugin_parser(xml);
-        expect(cfg.platforms.length).toBe(3);
-        expect(cfg.platforms.indexOf('android') > -1).toBe(true);
+        expect(cfg.platforms.length).toBe(1);
         expect(cfg.platforms.indexOf('ios') > -1).toBe(true);
-        expect(cfg.platforms.indexOf('blackberry') > -1).toBe(true);
     });
 });
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/spec/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/prepare.spec.js b/spec/prepare.spec.js
new file mode 100644
index 0000000..be0a1d3
--- /dev/null
+++ b/spec/prepare.spec.js
@@ -0,0 +1,133 @@
+/**
+    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 = require('../cordova'),
+    et = require('elementtree'),
+    shell = require('shelljs'),
+    path = require('path'),
+    fs = require('fs'),
+    config_parser = require('../src/config_parser'),
+    android_parser = require('../src/metadata/android_parser'),
+    ios_parser = require('../src/metadata/ios_parser'),
+    blackberry_parser = require('../src/metadata/blackberry_parser'),
+    hooker = require('../src/hooker'),
+    fixtures = path.join(__dirname, 'fixtures'),
+    hooks = path.join(fixtures, 'hooks'),
+    tempDir = path.join(__dirname, '..', 'temp'),
+    cordova_project = path.join(fixtures, 'projects', 'cordova');
+
+var cwd = process.cwd();
+
+describe('prepare command', function() {
+    beforeEach(function() {
+        shell.rm('-rf', tempDir);
+        shell.mkdir('-p', tempDir);
+    });
+
+    it('should not run inside a Cordova-based project with no added platforms', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        cordova.create(tempDir);
+        process.chdir(tempDir);
+        expect(function() {
+            cordova.prepare();
+        }).toThrow();
+    });
+    
+    it('should run inside a Cordova-based project with at least one added platform', function() {
+        // move platform project fixtures over to fake cordova into thinking platforms were added
+        // TODO: possibly add this to helper?
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+        shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+        this.after(function() {
+            process.chdir(cwd);
+            shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+            shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+        });
+
+        process.chdir(cordova_project);
+
+        var parser_spy = spyOn(android_parser.prototype, 'update_project');
+        expect(function() {
+            cordova.prepare();
+            expect(parser_spy).toHaveBeenCalled();
+        }).not.toThrow();
+    });
+    it('should not run outside of a Cordova-based project', function() {
+        this.after(function() {
+            process.chdir(cwd);
+        });
+
+        shell.mkdir('-p', tempDir);
+        process.chdir(tempDir);
+
+        expect(function() {
+            cordova.prepare();
+        }).toThrow();
+    });
+
+    describe('hooks', function() {
+        var s;
+        beforeEach(function() {
+            s = spyOn(hooker.prototype, 'fire').andReturn(true);
+        });
+
+        describe('when platforms are added', function() {
+            beforeEach(function() {
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'blackberry'), path.join(tempDir));
+                shell.mv('-f', path.join(cordova_project, 'platforms', 'ios'), path.join(tempDir));
+                process.chdir(cordova_project);
+            });
+            afterEach(function() {
+                shell.mv('-f', path.join(tempDir, 'blackberry'), path.join(cordova_project, 'platforms', 'blackberry'));
+                shell.mv('-f', path.join(tempDir, 'ios'), path.join(cordova_project, 'platforms', 'ios'));
+                process.chdir(cwd);
+            });
+
+            it('should fire before hooks through the hooker module', function() {
+                cordova.prepare();
+                expect(s).toHaveBeenCalledWith('before_prepare');
+            });
+            it('should fire after hooks through the hooker module', function() {
+                var parser_spy = spyOn(android_parser.prototype, 'update_project');
+                cordova.prepare();
+                parser_spy.mostRecentCall.args[1](); // parser cb
+                expect(s).toHaveBeenCalledWith('after_prepare');
+            });
+        });
+
+        describe('with no platforms added', function() {
+            beforeEach(function() {
+                cordova.create(tempDir);
+                process.chdir(tempDir);
+            });
+            afterEach(function() {
+                process.chdir(cwd);
+            });
+            it('should not fire the hooker', function() {
+                expect(function() {
+                    cordova.prepare();
+                }).toThrow();
+                expect(s).not.toHaveBeenCalledWith('before_prepare');
+                expect(s).not.toHaveBeenCalledWith('after_prepare');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/src/compile.js
----------------------------------------------------------------------
diff --git a/src/compile.js b/src/compile.js
index 0677c41..1833dce 100644
--- a/src/compile.js
+++ b/src/compile.js
@@ -46,7 +46,7 @@ function shell_out_to_debug(projectRoot, platform, callback) {
         if (code > 0) {
             throw new Error('An error occurred while building the ' + platform + ' project. ' + output);
         } else {
-            callback();
+            if (callback) callback();
         }
     });
 }
@@ -87,7 +87,7 @@ module.exports = function compile(platforms, callback) {
 
     // Iterate over each added platform
     platforms.forEach(function(platform) {
-        shell_out_to_debug(projectRoot, platform);
+        shell_out_to_debug(projectRoot, platform, end);
     });
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/src/metadata/android_parser.js
----------------------------------------------------------------------
diff --git a/src/metadata/android_parser.js b/src/metadata/android_parser.js
index b47b6a0..a79101f 100644
--- a/src/metadata/android_parser.js
+++ b/src/metadata/android_parser.js
@@ -63,7 +63,7 @@ module.exports.check_requirements = function(callback) {
 module.exports.prototype = {
     update_from_config:function(config) {
         if (config instanceof config_parser) {
-        } else throw 'update_from_config requires a config_parser object';
+        } else throw new Error('update_from_config requires a config_parser object');
 
         // Update app name by editing res/values/strings.xml
         var name = config.name();
@@ -144,9 +144,10 @@ module.exports.prototype = {
         // delete any .svn folders copied over
         util.deleteSvnFolders(platformWww);
     },
-    update_project:function(cfg) {
+    update_project:function(cfg, callback) {
         this.update_from_config(cfg);
         this.update_www();
+        if (callback) callback();
     }
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/src/platform.js
----------------------------------------------------------------------
diff --git a/src/platform.js b/src/platform.js
index caab916..cd3098b 100644
--- a/src/platform.js
+++ b/src/platform.js
@@ -32,7 +32,7 @@ module.exports = function platform(command, targets, callback) {
     var projectRoot = cordova_util.isCordova(process.cwd());
 
     if (!projectRoot) {
-        throw 'Current working directory is not a Cordova-based project.';
+        throw new Error('Current working directory is not a Cordova-based project.');
     }
 
     var hooks = new hooker(projectRoot),

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/61d29e13/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
index 9e5888b..36a1604 100644
--- a/src/prepare.js
+++ b/src/prepare.js
@@ -16,21 +16,27 @@
     specific language governing permissions and limitations
     under the License.
 */
-var cordova_util  = require('./util'),
-    path          = require('path'),
-    config_parser = require('./config_parser'),
-    platform      = require('./platform'),
-    fs            = require('fs'),
-    shell         = require('shelljs'),
-    ls            = fs.readdirSync,
-    et            = require('elementtree'),
-    android_parser= require('./metadata/android_parser'),
-    blackberry_parser= require('./metadata/blackberry_parser'),
-    ios_parser    = require('./metadata/ios_parser'),
-    hooker        = require('./hooker'),
-    n             = require('ncallbacks'),
-    prompt        = require('prompt'),
-    util          = require('util');
+var cordova_util      = require('./util'),
+    path              = require('path'),
+    config_parser     = require('./config_parser'),
+    platform          = require('./platform'),
+    fs                = require('fs'),
+    shell             = require('shelljs'),
+    ls                = fs.readdirSync,
+    et                = require('elementtree'),
+    android_parser    = require('./metadata/android_parser'),
+    blackberry_parser = require('./metadata/blackberry_parser'),
+    ios_parser        = require('./metadata/ios_parser'),
+    hooker            = require('./hooker'),
+    n                 = require('ncallbacks'),
+    prompt            = require('prompt'),
+    util              = require('util');
+
+var parsers = {
+    "android":android_parser,
+    "ios":ios_parser,
+    "blackberry":blackberry_parser
+};
 
 module.exports = function prepare(platforms, callback) {
     var projectRoot = cordova_util.isCordova(process.cwd());
@@ -67,30 +73,8 @@ module.exports = function prepare(platforms, callback) {
 
     // Iterate over each added platform
     platforms.forEach(function(platform) {
-        // Figure out paths based on platform
-        var parser, platformPath;
-        switch (platform) {
-            case 'android':
-                platformPath = path.join(projectRoot, 'platforms', 'android');
-                parser = new android_parser(platformPath);
-
-                // Update the related platform project from the config
-                parser.update_project(cfg);
-                break;
-            case 'blackberry':
-                platformPath = path.join(projectRoot, 'platforms', 'blackberry');
-                parser = new blackberry_parser(platformPath);
-
-                // Update the related platform project from the config
-                parser.update_project(cfg);
-                break;
-            case 'ios':
-                platformPath = path.join(projectRoot, 'platforms', 'ios');
-                parser = new ios_parser(platformPath);
-
-                // Update the related platform project from the config
-                parser.update_project(cfg);
-                break;
-        }
+        var platformPath = path.join(projectRoot, 'platforms', 'android');
+        var parser = new parsers[platform](platformPath);
+        parser.update_project(cfg, end);
     });
 };