You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/03/28 21:12:03 UTC

git commit: CB-6272 Fix subdir bug + tests & meta fetch with a src directory

Repository: cordova-plugman
Updated Branches:
  refs/heads/master ac1160d47 -> 54fc8582c


CB-6272 Fix subdir bug + tests & meta fetch with a src directory

github: close #66


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/54fc8582
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/54fc8582
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/54fc8582

Branch: refs/heads/master
Commit: 54fc8582ca10b506d8268124f55718e9f31bc9d1
Parents: ac1160d
Author: jbondc <jb...@openmv.com>
Authored: Sat Mar 15 12:59:42 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Fri Mar 28 16:11:38 2014 -0400

----------------------------------------------------------------------
 spec/install.spec.js                            | 79 ++++++++++++++++----
 spec/platform.spec.js                           | 30 ++++----
 spec/plugins/AndroidJS/plugin.xml               |  2 +-
 spec/plugins/DummyPlugin/plugin.xml             |  2 +-
 spec/plugins/FaultyPlugin/plugin.xml            |  4 +-
 spec/plugins/dependencies/A/plugin.xml          |  2 +-
 spec/plugins/dependencies/B/plugin.xml          |  4 +-
 spec/plugins/dependencies/C/plugin.xml          |  2 +-
 spec/plugins/dependencies/D/plugin.xml          |  2 +-
 spec/plugins/dependencies/E/plugin.xml          |  2 +-
 spec/plugins/dependencies/meta/D/plugin.xml     | 61 +++++++++++++++
 .../dependencies/meta/D/src/android/D.java      |  0
 .../meta/D/src/ios/DPluginCommand.h             |  0
 .../meta/D/src/ios/DPluginCommand.m             |  0
 .../plugins/dependencies/meta/D/www/plugin-d.js |  0
 .../dependencies/meta/subdir/E/plugin.xml       | 57 ++++++++++++++
 .../meta/subdir/E/src/android/E.java            |  0
 .../meta/subdir/E/src/ios/EPluginCommand.h      |  0
 .../meta/subdir/E/src/ios/EPluginCommand.m      |  0
 .../dependencies/meta/subdir/E/www/plugin-e.js  |  0
 spec/plugins/dependencies/subdir/E/plugin.xml   |  2 +-
 spec/projects/blackberry10/www/config.xml       |  4 +-
 spec/uninstall.spec.js                          | 22 +++---
 src/fetch.js                                    |  4 +-
 src/install.js                                  | 62 +++++++++++----
 src/platforms/common.js                         |  6 +-
 src/prepare.js                                  |  2 +-
 src/uninstall.js                                |  6 +-
 src/util/plugins.js                             | 16 +++-
 29 files changed, 291 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index f552c49..c15d38f 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -13,13 +13,13 @@ var install = require('../src/install'),
     child_process = require('child_process'),
     semver  = require('semver'),
     Q = require('q'),
-    spec    = __dirname,	
+    spec    = __dirname,
     done    = false,
     srcProject = path.join(spec, 'projects', 'android_install'),
     project = path.join(os.tmpdir(), 'plugman-test', 'android_install'),
 
     plugins_dir = path.join(spec, 'plugins'),
-    plugins_install_dir = path.join(project, 'cordova', 'plugins'),	
+    plugins_install_dir = path.join(project, 'cordova', 'plugins'),
     plugins = {
         'DummyPlugin' : path.join(plugins_dir, 'DummyPlugin'),
         'EnginePlugin' : path.join(plugins_dir, 'EnginePlugin'),
@@ -27,6 +27,7 @@ var install = require('../src/install'),
         'ChildBrowser' : path.join(plugins_dir, 'ChildBrowser'),
         'VariablePlugin' : path.join(plugins_dir, 'VariablePlugin'),
         'A' : path.join(plugins_dir, 'dependencies', 'A'),
+        'B' : path.join(plugins_dir, 'dependencies', 'B'),
         'C' : path.join(plugins_dir, 'dependencies', 'C'),
         'F' : path.join(plugins_dir, 'dependencies', 'F'),
         'G' : path.join(plugins_dir, 'dependencies', 'G')
@@ -58,12 +59,12 @@ var fake = {
             if(id == plugins['A'])
                 return Q(id); // full path to plugin
 
-            return Q( path.join(plugins_dir, 'dependencies', id) ); 
+            return Q( path.join(plugins_dir, 'dependencies', id) );
         }
     }
 }
 
-describe('start', function() {	
+describe('start', function() {
     var prepare, config_queue_add, proc, actions_push, ca, emit;
 
     beforeEach(function() {
@@ -114,7 +115,7 @@ describe('start', function() {
                 events.emit("verbose", "***** DONE START *****");
             }
         );
-        waitsFor(function() { return done; }, 'promise never resolved', 500);		
+        waitsFor(function() { return done; }, 'promise never resolved', 500);
     });
 });
 
@@ -172,11 +173,12 @@ describe('install', function() {
             });
         });
 
-        it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method after processing an install', function() {																												
+        it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method after processing an install', function() {
            expect(results['config_add']).toEqual([plugins_install_dir, dummy_id, 'android', {}, true]);
         });
-        it('should queue up actions as appropriate for that plugin and call process on the action stack', 			
-           function() {																											 				expect(results['actions_callCount']).toEqual(3);
+        it('should queue up actions as appropriate for that plugin and call process on the action stack',
+           function() {
+                expect(results['actions_callCount']).toEqual(3);
                 expect(results['actions_create']).toEqual([jasmine.any(Function), [jasmine.any(Object), path.join(plugins_install_dir, dummy_id), project, dummy_id], jasmine.any(Function), [jasmine.any(Object), project, dummy_id]]);
         });
 
@@ -288,14 +290,14 @@ describe('install', function() {
                 exec.andCallFake(function(cmd, cb) {
                     cb(null, '9.0.0\n');
                 });
-            });											   
-   
+            });
+
             it('should install any dependent plugins if missing', function() {
                 runs(function() {
                     installPromise( install('android', project, plugins['A']) );
                 });
                 waitsFor(function() { return done; }, 'install promise never resolved', 200);
-                runs(function() {							  
+                runs(function() {
                     // Look for 'Installing plugin ...' in events
                     var install = common.spy.getInstall(emit);
 
@@ -330,10 +332,10 @@ describe('install', function() {
                 runs(function () {
                     installPromise(install('android', project, plugins['F']));
                 });
-                waitsFor(function () { return done; }, 'install promise never resolved', 500);
+                waitsFor(function () { return done; }, 'install promise never resolved', 200);
                 runs(function () {
                     var install = common.spy.getInstall(emit);
-    
+
                     expect(install).toEqual([
                         'Install start for "C" on android.',
                         'Install start for "D" on android.',
@@ -348,13 +350,58 @@ describe('install', function() {
                 runs(function () {
                     installPromise( install('android', project, plugins['G']) );
                 });
-                waitsFor(function () { return done; }, 'install promise never resolved', 500);
+                waitsFor(function () { return done; }, 'install promise never resolved', 200);
                 runs(function () {
                     var install = common.spy.getInstall(emit);
        
                     expect(done.message).toEqual('Cyclic dependency from G to H');
                 });
-            });				
+            });
+
+            it('install subdir relative to top level plugin if no fetch meta', function() {
+                runs(function () {
+                    installPromise(install('android', project, plugins['B']));
+                });
+                waitsFor(function () { return done; }, 'install promise never resolved', 200);
+                runs(function () {
+                    var install = common.spy.getInstall(emit);
+
+                    expect(install).toEqual([
+                        'Install start for "D" on android.',
+                        'Install start for "E" on android.',
+                        'Install start for "B" on android.'
+                    ]);
+                });
+            });
+
+            it('install uses meta data (if available) of top level plugin source', function() {
+                // Fake metadata so plugin 'B' appears from 'meta/B'
+                var meta = require('../src/util/metadata');
+                spyOn(meta, 'get_fetch_metadata').andCallFake(function(){
+                    return {
+                        source: {type: 'dir', url: path.join(plugins['B'], '..', 'meta')}
+                    };
+                });
+
+                runs(function () {
+                    installPromise(install('android', project, plugins['B']));
+                });
+                waitsFor(function () { return done; }, 'install promise never resolved', 200);
+                runs(function () {
+                    var install = common.spy.getInstall(emit);
+
+                    expect(install).toEqual([
+                        'Install start for "D" on android.',
+                        'Install start for "E" on android.',
+                        'Install start for "B" on android.'
+                    ]);
+
+                    var copy = common.spy.startsWith(emit, "Copying from");
+                    expect(copy.length).toBe(3);
+                    expect(copy[0].indexOf(path.normalize('meta/D')) > 0).toBe(true);
+                    expect(copy[1].indexOf(path.normalize('meta/subdir/E')) > 0).toBe(true);
+                });
+            });
         });
 
     });
@@ -417,7 +464,7 @@ describe('end', function() {
                 events.emit('error', err);
 
             shell.rm('-rf', project);
-            done = true;	
+            done = true;
         });
 
         waitsFor(function() { return done; }, 'promise never resolved', 500);

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform.spec.js b/spec/platform.spec.js
index e737b5a..4f8099c 100644
--- a/spec/platform.spec.js
+++ b/spec/platform.spec.js
@@ -2,24 +2,24 @@ var platforms = require('../src/platforms')
 var pluginTags = ["source-file", "header-file", "lib-file", "resource-file", "framework"];
 
 function getTest(platformId, pluginTag) {
-	return function() {
-	    it('should exist', function() {
-	        expect(platforms[platformId][pluginTag] ).toBeDefined();
-	    });
-	   	it('with an install method', function() {
-	        expect(platforms[platformId][pluginTag].install ).toBeDefined();
-	    });
-	   	it('with an uninstall method', function() {
-	        expect(platforms[platformId][pluginTag].uninstall ).toBeDefined();
-	    });
-	}
+    return function() {
+        it('should exist', function() {
+            expect(platforms[platformId][pluginTag] ).toBeDefined();
+        });
+        it('with an install method', function() {
+            expect(platforms[platformId][pluginTag].install ).toBeDefined();
+        });
+        it('with an uninstall method', function() {
+            expect(platforms[platformId][pluginTag].uninstall ).toBeDefined();
+        });
+    }
 }
 
 for(var platformId in platforms) {
-	for(var index = 0, len = pluginTags.length; index < len; index++) {
-		var funk = getTest(platformId,pluginTags[index]);
-		describe(platformId + " should have a " + pluginTags[index] + " object", funk);
-	}
+    for(var index = 0, len = pluginTags.length; index < len; index++) {
+        var funk = getTest(platformId,pluginTags[index]);
+        describe(platformId + " should have a " + pluginTags[index] + " object", funk);
+    }
 
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/AndroidJS/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/AndroidJS/plugin.xml b/spec/plugins/AndroidJS/plugin.xml
index f5b9f1a..1a68749 100644
--- a/spec/plugins/AndroidJS/plugin.xml
+++ b/spec/plugins/AndroidJS/plugin.xml
@@ -27,7 +27,7 @@
 
     <!-- android -->
     <platform name="android">
-		<js-module src="www/android.js" name="Android">
+        <js-module src="www/android.js" name="Android">
             <clobbers target="android" />
         </js-module>
     </platform>

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/DummyPlugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/DummyPlugin/plugin.xml b/spec/plugins/DummyPlugin/plugin.xml
index dcec3bf..4733afb 100644
--- a/spec/plugins/DummyPlugin/plugin.xml
+++ b/spec/plugins/DummyPlugin/plugin.xml
@@ -39,7 +39,7 @@
         <access origin="build.phonegap.com" />
         <access origin="s3.amazonaws.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="AndroidManifest.xml" parent="/manifest/application">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/FaultyPlugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/FaultyPlugin/plugin.xml b/spec/plugins/FaultyPlugin/plugin.xml
index e4c8eec..628496d 100644
--- a/spec/plugins/FaultyPlugin/plugin.xml
+++ b/spec/plugins/FaultyPlugin/plugin.xml
@@ -25,8 +25,8 @@
 
     <name>Faulty Plugin</name>
 
-	<access origin="build.phonegap.com" />
-	<access origin="s3.amazonaws.com" />
+    <access origin="build.phonegap.com" />
+    <access origin="s3.amazonaws.com" />
     <!-- file doesn't exist -->
 
     <config-file target="config.xml" parent="/widget">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/A/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/A/plugin.xml b/spec/plugins/dependencies/A/plugin.xml
index a8acbfb..ec83e8c 100644
--- a/spec/plugins/dependencies/A/plugin.xml
+++ b/spec/plugins/dependencies/A/plugin.xml
@@ -33,7 +33,7 @@
     <config-file target="config.xml" parent="/*">
         <access origin="build.phonegap.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="res/xml/config.xml" parent="plugins">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/B/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/B/plugin.xml b/spec/plugins/dependencies/B/plugin.xml
index 16847a5..ee32e2d 100644
--- a/spec/plugins/dependencies/B/plugin.xml
+++ b/spec/plugins/dependencies/B/plugin.xml
@@ -25,8 +25,8 @@
 
     <name>Plugin B</name>
 
-    <dependency id="D" url="." subdir="spec/plugins/dependencies/D"/>
-    <dependency id="E" url="." subdir="spec/plugins/dependencies/subdir/E"/>
+    <dependency id="D" url="." subdir="D"/>
+    <dependency id="E" url="." subdir="subdir/E"/>
 
     <asset src="www/plugin-b.js" target="plugin-b.js" />
 

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/C/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/C/plugin.xml b/spec/plugins/dependencies/C/plugin.xml
index 578e57d..88c2d2c 100644
--- a/spec/plugins/dependencies/C/plugin.xml
+++ b/spec/plugins/dependencies/C/plugin.xml
@@ -30,7 +30,7 @@
     <config-file target="config.xml" parent="/*">
         <access origin="build.phonegap.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="res/xml/config.xml" parent="plugins">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/D/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/D/plugin.xml b/spec/plugins/dependencies/D/plugin.xml
index c43a9f8..f07b063 100644
--- a/spec/plugins/dependencies/D/plugin.xml
+++ b/spec/plugins/dependencies/D/plugin.xml
@@ -30,7 +30,7 @@
     <config-file target="config.xml" parent="/*">
         <access origin="build.phonegap.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="res/xml/config.xml" parent="plugins">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/E/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/plugin.xml b/spec/plugins/dependencies/E/plugin.xml
index 586c74e..bb28fa1 100644
--- a/spec/plugins/dependencies/E/plugin.xml
+++ b/spec/plugins/dependencies/E/plugin.xml
@@ -32,7 +32,7 @@
     <config-file target="config.xml" parent="/*">
         <access origin="build.phonegap.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="res/xml/config.xml" parent="plugins">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/D/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/D/plugin.xml b/spec/plugins/dependencies/meta/D/plugin.xml
new file mode 100644
index 0000000..941bd57
--- /dev/null
+++ b/spec/plugins/dependencies/meta/D/plugin.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="D"
+    version="0.6.0">
+
+    <name>Plugin D</name>
+
+    <asset src="www/plugin-d.js" target="plugin-d.js" />
+
+    <config-file target="config.xml" parent="/*">
+        <access origin="build.phonegap.com" />
+    </config-file>
+
+    <engines>
+        <engine name="cordova" version=">=1.0.0"/>
+    </engines>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="plugins">
+            <plugin name="D"
+                value="com.phonegap.D.D"/>
+        </config-file>
+
+        <source-file src="src/android/D.java"
+                target-dir="src/com/phonegap/D" />
+    </platform>
+
+        
+    <!-- ios -->
+    <platform name="ios">
+        <!-- CDV 2.5+ -->
+        <config-file target="config.xml" parent="plugins">
+            <plugin name="D"
+                value="DPluginCommand"/>
+        </config-file>
+
+        <header-file src="src/ios/DPluginCommand.h" />
+        <source-file src="src/ios/DPluginCommand.m"/>
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/D/src/android/D.java
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/D/src/android/D.java b/spec/plugins/dependencies/meta/D/src/android/D.java
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.h
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.h b/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.h
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.m
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.m b/spec/plugins/dependencies/meta/D/src/ios/DPluginCommand.m
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/D/www/plugin-d.js
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/D/www/plugin-d.js b/spec/plugins/dependencies/meta/D/www/plugin-d.js
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/subdir/E/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/subdir/E/plugin.xml b/spec/plugins/dependencies/meta/subdir/E/plugin.xml
new file mode 100644
index 0000000..57d96d9
--- /dev/null
+++ b/spec/plugins/dependencies/meta/subdir/E/plugin.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="E"
+    version="0.6.0">
+
+    <name>Plugin E</name>
+
+    <asset src="www/plugin-e.js" target="plugin-e.js" />
+
+    <config-file target="config.xml" parent="/*">
+        <access origin="build.phonegap.com" />
+    </config-file>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="plugins">
+            <plugin name="E"
+                value="com.phonegap.E.E"/>
+        </config-file>
+
+        <source-file src="src/android/E.java"
+                target-dir="src/com/phonegap/E" />
+    </platform>
+
+        
+    <!-- ios -->
+    <platform name="ios">
+        <!-- CDV 2.5+ -->
+        <config-file target="config.xml" parent="plugins">
+            <plugin name="E"
+                value="EPluginCommand"/>
+        </config-file>
+
+        <header-file src="src/ios/EPluginCommand.h" />
+        <source-file src="src/ios/EPluginCommand.m"/>
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/subdir/E/src/android/E.java
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/subdir/E/src/android/E.java b/spec/plugins/dependencies/meta/subdir/E/src/android/E.java
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.h
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.h b/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.h
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.m
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.m b/spec/plugins/dependencies/meta/subdir/E/src/ios/EPluginCommand.m
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/meta/subdir/E/www/plugin-e.js
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/meta/subdir/E/www/plugin-e.js b/spec/plugins/dependencies/meta/subdir/E/www/plugin-e.js
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/plugins/dependencies/subdir/E/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/plugin.xml b/spec/plugins/dependencies/subdir/E/plugin.xml
index c7a2098..57d96d9 100644
--- a/spec/plugins/dependencies/subdir/E/plugin.xml
+++ b/spec/plugins/dependencies/subdir/E/plugin.xml
@@ -30,7 +30,7 @@
     <config-file target="config.xml" parent="/*">
         <access origin="build.phonegap.com" />
     </config-file>
-	
+
     <!-- android -->
     <platform name="android">
         <config-file target="res/xml/config.xml" parent="plugins">

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/projects/blackberry10/www/config.xml
----------------------------------------------------------------------
diff --git a/spec/projects/blackberry10/www/config.xml b/spec/projects/blackberry10/www/config.xml
index 6b132c2..1dc8fe8 100644
--- a/spec/projects/blackberry10/www/config.xml
+++ b/spec/projects/blackberry10/www/config.xml
@@ -24,7 +24,7 @@
 
 <widget xmlns="http://www.w3.org/ns/widgets"
         xmlns:rim="http://www.blackberry.com/ns/widgets"
-	version="1.0.0.0" id="cordovaExample">
+  version="1.0.0.0" id="cordovaExample">
 
   <name>cordovaExample</name>
 
@@ -79,7 +79,7 @@
 
   <rim:loadingScreen backgroundColor="#CFCFCF"
                      foregroundImage="res/screen/blackberry/screen-225.png"
-		     onFirstLaunch="true">
+                     onFirstLaunch="true">
     <rim:transitionEffect type="fadeOut" />
   </rim:loadingScreen>
 

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index bf9aa7f..875b609 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -16,8 +16,8 @@ var uninstall = require('../src/uninstall'),
     project2 = path.join(spec, 'projects', 'android_uninstall.test2'),
 
     plugins_dir = path.join(spec, 'plugins'),
-    plugins_install_dir = path.join(project, 'cordova', 'plugins'),	
-    plugins_install_dir2 = path.join(project2, 'cordova', 'plugins'),	
+    plugins_install_dir = path.join(project, 'cordova', 'plugins'),
+    plugins_install_dir2 = path.join(project2, 'cordova', 'plugins'),
 
     plugins = {
         'DummyPlugin' : path.join(plugins_dir, 'DummyPlugin'),
@@ -25,13 +25,13 @@ var uninstall = require('../src/uninstall'),
         'C' : path.join(plugins_dir, 'dependencies', 'C')
     },
     promise,
-    dummy_id = 'com.phonegap.plugins.dummyplugin';	
+    dummy_id = 'com.phonegap.plugins.dummyplugin';
 
 function uninstallPromise(f) {
     return f.then(function() { done = true; }, function(err) { done = err; });
 }
 
-describe('start', function() {	
+describe('start', function() {
 
     it('start', function() {
         shell.rm('-rf', project);
@@ -56,13 +56,13 @@ describe('start', function() {
     });
 });
 
-describe('uninstallPlatform', function() {								   
+describe('uninstallPlatform', function() {
     var proc, prepare, actions_push, add_to_queue, c_a, rm;
     var fsWrite;
 
     var plat_common = require('../src/platforms/common');
 
-    beforeEach(function() {	
+    beforeEach(function() {
         proc = spyOn(actions.prototype, 'process').andReturn(Q());
         actions_push = spyOn(actions.prototype, 'push');
         c_a = spyOn(actions.prototype, 'createAction');
@@ -88,7 +88,7 @@ describe('uninstallPlatform', function() {
                 uninstallPromise(uninstall.uninstallPlatform('android', project, dummy_id));
             });
             waitsFor(function() { return done; }, 'promise never resolved', 200);
-            runs(function() {	  
+            runs(function() {
                 expect(add_to_queue).toHaveBeenCalledWith(plugins_install_dir, dummy_id, 'android', true);
             });
         });
@@ -112,7 +112,7 @@ describe('uninstallPlatform', function() {
                 runs(function() {
                     uninstallPromise(uninstall.uninstallPlatform('android', project, 'A'));
                 });
-                waitsFor(function() { return done; }, 'promise never resolved', 200);	
+                waitsFor(function() { return done; }, 'promise never resolved', 200);
                 runs(function() {
                     expect(emit).toHaveBeenCalledWith('log', 'Uninstalling 2 dependent plugins.');
                 });
@@ -142,7 +142,7 @@ describe('uninstallPlatform', function() {
     });
 });
 
-describe('uninstallPlugin', function() {							 
+describe('uninstallPlugin', function() {
     var rm, fsWrite, rmstack = [], emit;
 
     beforeEach(function() {
@@ -190,7 +190,7 @@ describe('uninstallPlugin', function() {
                 var del = common.spy.getDeleted(emit);
                 expect(del).toEqual(['Deleted "C"']);
             });
-        });		
+        });
 
         it("never remove top level plugins if they are a dependency", function() {  
             runs(function() {
@@ -281,7 +281,7 @@ describe('end', function() {
 
             shell.rm('-rf', project);
             shell.rm('-rf', project2);
-            done = true;	
+            done = true;
         });
 
         waitsFor(function() { return done; }, 'promise never resolved', 500);

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/fetch.js
----------------------------------------------------------------------
diff --git a/src/fetch.js b/src/fetch.js
index a3adb3e..5b245ad 100644
--- a/src/fetch.js
+++ b/src/fetch.js
@@ -55,7 +55,7 @@ module.exports = function fetchPlugin(plugin_src, plugins_dir, options) {
                 }
             };
 
-            return plugins.clonePluginGitRepo(plugin_src, plugins_dir, options.subdir, options.git_ref)
+            return plugins.clonePluginGit(plugin_src, plugins_dir, options)
             .then(function(dir) {
                 return checkID(options.expected_id, dir);
             })
@@ -90,6 +90,8 @@ module.exports = function fetchPlugin(plugin_src, plugins_dir, options) {
 
         return p
         .then(function(dir) {
+                options.plugin_src_dir = dir;
+
                 return copyPlugin(dir, plugins_dir, options.link && linkable);
             })
         .then(function(dir) {

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index cc1b8f0..277ef83 100644
--- a/src/install.js
+++ b/src/install.js
@@ -137,7 +137,7 @@ function callEngineScripts(engines) {
         engines.map(function(engine){
             // CB-5192; on Windows scriptSrc doesn't have file extension so we shouldn't check whether the script exists
 
-            var scriptPath = engine.scriptSrc ? '"' + engine.scriptSrc + '"' : null;		
+            var scriptPath = engine.scriptSrc ? '"' + engine.scriptSrc + '"' : null;
 
             if(scriptPath && (isWindows || fs.existsSync(engine.scriptSrc)) ) {
 
@@ -236,8 +236,8 @@ var runInstall = module.exports.runInstall = function runInstall(actions, platfo
       , filtered_variables = {};
     var name         = plugin_et.findall('name').text;
     var plugin_id    = plugin_et.getroot().attrib['id'];
-    options = options || {};
 
+    options = options || {};
     options.graph = options.graph || new dep_graph();
 
     if (isPluginInstalled(plugins_dir, platform, plugin_id)) {
@@ -313,7 +313,7 @@ var runInstall = module.exports.runInstall = function runInstall(actions, platfo
 function installDependencies(install, dependencies, options) {
     events.emit('verbose', 'Dependencies detected, iterating through them...');
 
-    var top_plugins = path.join(install.top_plugin_dir, '..');
+    var top_plugins = path.join(options.plugin_src_dir || install.top_plugin_dir, '..')
 
     // Add directory of top-level plugin to search path
     options.searchpath = options.searchpath || [];
@@ -330,13 +330,13 @@ function installDependencies(install, dependencies, options) {
             function() {   
                 var dep = {
                     id: depXml.attrib.id,
-                    subdir: depXml.attrib.subdir,
+                    subdir: depXml.attrib.subdir || '',
                     url: depXml.attrib.url || '',
                     git_ref: depXml.attrib.commit
                 }
 
-                if (dep.subdir) {
-                    dep.subdir = path.join(dep.subdir.split('/'));
+                if (dep.subdir.length) {
+                    dep.subdir = path.normalize(dep.subdir);
                 }
 
                 if (!dep.id) {
@@ -347,9 +347,9 @@ function installDependencies(install, dependencies, options) {
                 options.graph.add(install.top_plugin_id, dep.id);
                 options.graph.getChain(install.top_plugin_id);
 
-                return tryFetchDependency(dep, install)
+                return tryFetchDependency(dep, install, options)
                 .then( 
-                    function(url){						
+                    function(url){
                         dep.url = url;
                         return installDependency(dep, install, options);
                     }
@@ -360,7 +360,7 @@ function installDependencies(install, dependencies, options) {
     }, Q(true));
 }
 
-function tryFetchDependency(dep, install) {
+function tryFetchDependency(dep, install, options) {
 
     // Handle relative dependency paths by expanding and resolving them.
     // The easy case of relative paths is to have a URL of '.' and a different subdir.
@@ -371,7 +371,12 @@ function tryFetchDependency(dep, install) {
         // Look up the parent plugin's fetch metadata and determine the correct URL.
         var fetchdata = require('./util/metadata').get_fetch_metadata(install.top_plugin_dir);
         if (!fetchdata || !(fetchdata.source && fetchdata.source.type)) {
-            throw new Error('No fetch metadata found for plugin ' + install.top_plugin_id + '. Cannot install relative dependency --> ' + dep.id);
+
+            var relativePath = dep.subdir || dep.id;
+
+            events.emit('warn', 'No fetch metadata found for plugin ' + install.top_plugin_id + '. checking for ' + relativePath + ' in '+ options.searchpath.join(','));
+
+            return Q(relativePath);
         }
 
         // Now there are two cases here: local directory, and git URL.
@@ -399,11 +404,38 @@ function tryFetchDependency(dep, install) {
                 return Q(url);
             }).fail(function(error){
 //console.log("Failed to resolve url='.': " + error);
-                return Q(dep.url);	
+                return Q(dep.url);
             });
 
         } else if (fetchdata.source.type === 'git') {
             return Q(fetchdata.source.url);
+        } else if (fetchdata.source.type === 'dir') {
+
+            // Note: With fetch() independant from install()
+            // $md5 = md5(uri)
+            // Need a Hash(uri) --> $tmpDir/cordova-fetch/git-hostname.com-$md5/
+            // plugin[id].install.source --> searchpath that matches fetch uri
+
+            // mapping to a directory of OS containing fetched plugins
+            var tmpDir = fetchdata.source.url;
+            tmpDir = tmpDir.replace('$tmpDir', os.tmpdir());
+
+            var pluginSrc = '';
+            if(dep.subdir.length) {
+                // Plugin is relative to directory
+                pluginSrc = path.join(tmpDir, dep.subdir);
+            }
+
+            // Try searchpath in dir, if that fails re-fetch
+            if( !pluginSrc.length || !fs.existsSync(pluginSrc) ) {
+                pluginSrc = dep.id;
+
+                // Add search path
+                if( options.searchpath.indexOf(tmpDir) == -1 )
+                    options.searchpath.unshift(tmpDir); // place at top of search
+            }
+
+            return Q( pluginSrc );
         }
     }
 
@@ -418,10 +450,10 @@ function tryFetchDependency(dep, install) {
 
     // CB-4770: registry fetching
     if(dep.url === undefined) {
-        dep.url = dep.plugin_id;							
-    }	
+        dep.url = dep.id;
+    }
 
-    return Q(dep.url);	
+    return Q(dep.url);
 }
 
 function installDependency(dep, install, options) {
@@ -552,7 +584,7 @@ function isAbsolutePath(path) {
 }
 
 function isRelativePath(path) {
-    return !isAbsolutePath();	
+    return !isAbsolutePath();
 }
 
 function readId(plugin_dir) {

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/platforms/common.js
----------------------------------------------------------------------
diff --git a/src/platforms/common.js b/src/platforms/common.js
index 8fe1730..e7ca241 100644
--- a/src/platforms/common.js
+++ b/src/platforms/common.js
@@ -1,7 +1,7 @@
 var shell = require('shelljs'),
     path  = require('path'),
     fs    = require('fs'),
-	common;
+    common;
 
 module.exports = common = {
     // helper for resolving source paths from plugin.xml
@@ -30,9 +30,9 @@ module.exports = common = {
     },
     // Same as copy file but throws error if target exists
     copyNewFile:function(plugin_dir, src, project_dir, dest) {
-		var target_path = common.resolveTargetPath(project_dir, dest);
+        var target_path = common.resolveTargetPath(project_dir, dest);
         if (fs.existsSync(target_path)) 
-			throw new Error('"' + target_path + '" already exists!');
+            throw new Error('"' + target_path + '" already exists!');
 
         common.copyFile(plugin_dir, src, project_dir, dest);
     },

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
index 3fbb7c0..361249d 100644
--- a/src/prepare.js
+++ b/src/prepare.js
@@ -154,7 +154,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
 
         // Copy www assets described in <asset> tags.
         assets = assets || [];
-        assets.forEach(function(asset) {					
+        assets.forEach(function(asset) {
             common.asset.install(asset, pluginDir, wwwDir);
         });
 

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index 98c57ad..1997abd 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -149,7 +149,7 @@ module.exports.uninstallPlugin = function(id, plugins_dir, options) {
                     return Q.reject( new Error(msg) );
                 } else {
                     events.emit('warn', msg +' and cannot be removed (hint: use -f or --force)');
-                    continue;	
+                    continue;
                 }
             }
         }
@@ -169,7 +169,7 @@ function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugin
     var plugin_et    = xml_helpers.parseElementtreeSync(xml_path);
     var plugin_id    = plugin_et._root.attrib['id'];
 
-    // deps info can be passed recusively
+    // Deps info can be passed recusively
     var depsInfo = options.depsInfo || dependencies.generate_dependency_info(plugins_dir, platform, 'remove');
 
     // Check that this plugin has no dependents.
@@ -178,7 +178,7 @@ function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugin
     if(options.is_top_level && dependents && dependents.length > 0) {
         var msg = "The plugin '"+ plugin_id +"' is required by (" + dependents.join(', ') + ")";
         if(options.force) {
-            events.emit("info", msg + " but forcing removal");	
+            events.emit("info", msg + " but forcing removal");
         } else {
             return Q.reject( new Error(msg + ", skipping uninstallation.") );
         }

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/54fc8582/src/util/plugins.js
----------------------------------------------------------------------
diff --git a/src/util/plugins.js b/src/util/plugins.js
index db49884..3801be7 100644
--- a/src/util/plugins.js
+++ b/src/util/plugins.js
@@ -25,18 +25,30 @@ var http = require('http'),
     shell = require('shelljs'),
     child_process = require('child_process'),
     Q = require('q'),
-    xml_helpers = require('./xml-helpers');
+    xml_helpers = require('./xml-helpers'),
+    tmp_dir;
 
 module.exports = {
     searchAndReplace:require('./search-and-replace'),
 
+    clonePluginGit:function(plugin_git_url, plugins_dir, options) {
+        return module.exports.clonePluginGitRepo(plugin_git_url, plugins_dir, options.subdir, options.git_ref).then(
+            function(dst){
+                // Keep location where we checked out git repo
+                options.plugin_src_dir = tmp_dir;
+                return dst;
+            }
+        );
+    },
+
     // Fetches plugin information from remote server.
     // Returns a promise.
     clonePluginGitRepo:function(plugin_git_url, plugins_dir, subdir, git_ref) {
+
         if(!shell.which('git')) {
             return Q.reject(new Error('"git" command line tool is not installed: make sure it is accessible on your PATH.'));
         }
-        var tmp_dir = path.join(os.tmpdir(), 'plugman-tmp' +(new Date).valueOf());
+        tmp_dir = path.join(os.tmpdir(), 'plugman', 'git', String((new Date).valueOf()));
 
         shell.rm('-rf', tmp_dir);