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/05/16 17:57:11 UTC

[12/20] git commit: differentiating between top-level and dependent plugins. uninstall should work for the single plugin case (still need to fire off dependent plugin danglin uninstalls).

differentiating between top-level and dependent plugins. uninstall should work for the single plugin case (still need to fire off dependent plugin danglin uninstalls).


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

Branch: refs/heads/master
Commit: be96c196d051ba1a2e3707151a3496e9339e9b59
Parents: adf3e1e
Author: Fil Maj <ma...@gmail.com>
Authored: Tue May 14 18:52:54 2013 -0700
Committer: Fil Maj <ma...@gmail.com>
Committed: Thu May 16 08:55:42 2013 -0700

----------------------------------------------------------------------
 main.js                    |    4 +-
 package.json               |    4 +-
 src/install.js             |   53 +++++++-------
 src/platforms/ios.js       |    1 -
 src/uninstall.js           |  156 +++++++++++++++++++--------------------
 src/util/action-stack.js   |    2 +-
 src/util/config-changes.js |   27 +++++--
 src/util/dependencies.js   |   34 +++++++++
 8 files changed, 161 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/main.js
----------------------------------------------------------------------
diff --git a/main.js b/main.js
index ca2e000..414cf18 100755
--- a/main.js
+++ b/main.js
@@ -63,7 +63,7 @@ else if (!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
     printUsage();
 }
 else if (cli_opts.uninstall) {
-    plugman.uninstall(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, cli_opts.www);
+    plugman.uninstall(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, {}, cli_opts.www, true /* is top level? */);
 }
 else {
     var cli_variables = {}
@@ -74,7 +74,7 @@ else {
             if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
         });
     }
-    plugman.install(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, '.', cli_variables, cli_opts.www);
+    plugman.install(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, '.', cli_variables, cli_opts.www, true /* is top level? */);
 }
 
 function printUsage() {

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 6380287..73ab275 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,9 @@
     "bplist-parser": "0.0.x",
     "shelljs": "0.1.x",
     "osenv": "0.0.x",
-    "ncallbacks":"1.1.0"
+    "ncallbacks":"1.1.0",
+    "underscore":"1.4.4",
+    "dep-graph":"1.1.0"
   },
   "devDependencies": {
     "jasmine-node": "1.7.0"

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index cd5652f..24f012d 100644
--- a/src/install.js
+++ b/src/install.js
@@ -6,14 +6,12 @@ var path = require('path'),
     action_stack = require('./util/action-stack'),
     platform_modules = require('./platforms');
 
-module.exports = function installPlugin(platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, callback) {
+module.exports = function installPlugin(platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, is_top_level, callback) {
     if (!platform_modules[platform]) {
         var err = new Error(platform + " not supported.");
-        if (callback) {
-            callback(err);
-            return;
-        }
+        if (callback) callback(err);
         else throw err;
+        return;
     }
 
     var plugin_dir = path.join(plugins_dir, id);
@@ -27,15 +25,15 @@ module.exports = function installPlugin(platform, project_dir, id, plugins_dir,
                 callback(err);
             } else {
                 // update ref to plugin_dir after successful fetch, via fetch callback
-                runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback);
+                runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback);
             }
         });
     } else {
-        runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback);
+        runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback);
     }
 };
 
-function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback) {
+function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback) {
     var xml_path     = path.join(plugin_dir, 'plugin.xml')
       , xml_text     = fs.readFileSync(xml_path, 'utf-8')
       , plugin_et    = new et.ElementTree(et.XML(xml_text))
@@ -46,14 +44,19 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
     // check if platform has plugin installed already.
     var platform_config = config_changes.get_platform_json(plugins_dir, platform);
     var plugin_basename = path.basename(plugin_dir);
-    var is_fully_installed = false;
+    var is_installed = false;
     Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) {
         if (installed_plugin_id == plugin_id) {
-            is_fully_installed = true;
+            is_installed = true;
+        }
+    });
+    Object.keys(platform_config.dependent_plugins).forEach(function(installed_plugin_id) {
+        if (installed_plugin_id == plugin_id) {
+            is_installed = true;
         }
     });
-    if (is_fully_installed) {
-        console.log('Plugin "' + plugin_id + '" already installed. Carry on.');
+    if (is_installed) {
+        console.log('Plugin "' + plugin_id + '" already installed, \'sall good.');
         if (callback) callback();
         return;
     }
@@ -80,7 +83,7 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
     var dependencies = plugin_et.findall('dependency');
     if (dependencies && dependencies.length) {
         var end = n(dependencies.length, function() {
-            handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, callback);
+            handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback);
         });
         dependencies.forEach(function(dep) {
             var dep_plugin_id = dep.attrib.id;
@@ -92,18 +95,18 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
 
             if (fs.existsSync(path.join(plugins_dir, dep_plugin_id))) {
                 console.log('Dependent plugin ' + dep.attrib.id + ' already fetched, using that version.');
-                module.exports(platform, project_dir, dep_plugin_id, plugins_dir, dep_subdir, filtered_variables, www_dir, end);
+                module.exports(platform, project_dir, dep_plugin_id, plugins_dir, dep_subdir, filtered_variables, www_dir, false, end);
             } else {
                 console.log('Dependent plugin ' + dep.attrib.id + ' not fetched, retrieving then installing.');
-                module.exports(platform, project_dir, dep_url, plugins_dir, dep_subdir, filtered_variables, www_dir, end);
+                module.exports(platform, project_dir, dep_url, plugins_dir, dep_subdir, filtered_variables, www_dir, false, end);
             }
         });
     } else {
-        handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, callback);
+        handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback);
     }
 }
 
-function handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, callback) {
+function handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback) {
     var handler = platform_modules[platform];
     www_dir = www_dir || handler.www_dir(project_dir);
 
@@ -153,16 +156,12 @@ function handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir,
                 console.log(info[0].text);
             }
 
-            finalizeInstall(project_dir, plugins_dir, platform, plugin_basename, filtered_variables, callback);
+            // queue up the plugin so prepare knows what to do.
+            config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_basename, platform, filtered_variables, is_top_level);
+            // call prepare after a successful install
+            require('./../plugman').prepare(project_dir, platform, plugins_dir);
+
+            if (callback) callback();
         }
     });
 }
-
-function finalizeInstall(project_dir, plugins_dir, platform, plugin_name, variables, callback) {
-    // queue up the plugin so prepare knows what to do.
-    config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_name, platform, variables);
-    // call prepare after a successful install
-    require('./../plugman').prepare(project_dir, platform, plugins_dir);
-
-    if (callback) callback();
-}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/platforms/ios.js
----------------------------------------------------------------------
diff --git a/src/platforms/ios.js b/src/platforms/ios.js
index 68376b3..adc0325 100644
--- a/src/platforms/ios.js
+++ b/src/platforms/ios.js
@@ -72,7 +72,6 @@ module.exports = {
         },
         uninstall:function(header_el, project_dir, project) {
             var src = header_el.attrib['src'];
-            var srcFile = path.resolve(plugin_dir, src);
             var targetDir = path.resolve(project.plugins_dir, getRelativeDir(header_el));
             var destFile = path.resolve(targetDir, path.basename(src));
             project.xcode.removeHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile)));

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index 35838ca..ee0cd62 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -2,9 +2,12 @@ var path = require('path'),
     fs   = require('fs'),
     et   = require('elementtree'),
     config_changes = require('./util/config-changes'),
+    action_stack = require('./util/action-stack'),
+    dependencies = require('./util/dependencies'),
+    underscore = require('underscore'),
     platform_modules = require('./platforms');
 
-module.exports = function uninstallPlugin(platform, project_dir, name, plugins_dir, cli_variables, www_dir, callback) {
+module.exports = function uninstallPlugin(platform, project_dir, id, plugins_dir, cli_variables, www_dir, is_top_level, callback) {
     if (!platform_modules[platform]) {
         var err = new Error(platform + " not supported.");
         if (callback) callback(err);
@@ -12,8 +15,7 @@ module.exports = function uninstallPlugin(platform, project_dir, name, plugins_d
         return;
     }
 
-    // Check that the plugin has already been fetched.
-    var plugin_dir = path.join(plugins_dir, name);
+    var plugin_dir = path.join(plugins_dir, id);
 
     if (!fs.existsSync(plugin_dir)) {
         var err = new Error('Plugin "' + name + '" not found. Already uninstalled?');
@@ -22,101 +24,97 @@ module.exports = function uninstallPlugin(platform, project_dir, name, plugins_d
         return;
     }
 
-    runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback);
+    runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback);
 };
 
-function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback) {
+function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback) {
     var xml_path     = path.join(plugin_dir, 'plugin.xml')
       , xml_text     = fs.readFileSync(xml_path, 'utf-8')
       , plugin_et    = new et.ElementTree(et.XML(xml_text))
     var name         = plugin_et.findall('name').text;
     var plugin_id    = plugin_et._root.attrib['id'];
 
-    var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
-    if (!platformTag) {
-        // Either this plugin doesn't support this platform, or it's a JS-only plugin.
-        // Either way, return now.
-        // should call prepare probably!
-        require('./../plugman').prepare(project_dir, platform, plugins_dir);
-        if (callback) callback();
-        return;
+    var dependency_info = dependencies.generate_dependency_info(plugins_dir, platform);
+    var graph = dependency_info.graph;
+    var dependents = graph.getChain(plugin_id);
+
+    var tlps = dependency_info.top_level_plugins;
+    var diff_arr = [];
+    tlps.forEach(function(tlp) {
+        if (tlp != plugin_id) {
+            var ds = graph.getChain(tlp);
+            if (is_top_level && ds.indexOf(plugin_id) > -1) {
+                // Another top-level plugin depends on this one
+                // Cannot uninstall then.
+                console.log('Another top-level plugin (' + tlp + ') relies on plugin ' + plugin_id + ', therefore, aborting uninstallation.');
+                if (callback) callback();
+                return;
+            }
+            diff_arr.push(ds);
+        }
+    });
+
+    if (dependents.length) {
+        // this plugin has dependencies
+        diff_arr.unshift(dependents);
+        // do a set difference to determine which dependencies are not required by other existing plugins
+        var danglers = underscore.difference.apply(null, diff_arr);
+        danglers && danglers.forEach(function(dangle) {
+            // TODO: fire off an uninstall for each danglin' dep
+        });
+    } else {
+        // this plugin is bare, uninstall it!
+        handleUninstall(platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level, callback);
     }
+}
 
+function handleUninstall(platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level, callback) {
     var platform_modules = require('./platforms');
-    // parse plugin.xml into transactions
     var handler = platform_modules[platform];
-    var txs = [];
-    var sourceFiles = platformTag.findall('./source-file'),
-        headerFiles = platformTag.findall('./header-file'),
-        resourceFiles = platformTag.findall('./resource-file'),
-        assets = platformTag.findall('./asset'),
-        frameworks = platformTag.findall('./framework');
-    assets = assets.concat(plugin_et.findall('./asset'));
+    var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
     www_dir = www_dir || handler.www_dir(project_dir);
 
-    // asset uninstallation
-    var uninstalledAssets = [];
-    var common = require('./platforms/common');
-    try {
-        for(var i = 0, j = assets.length ; i < j ; i++) {
-            common.removeFile(www_dir, assets[i].attrib['target']);
-            uninstalledAssets.push(assets[i]);
-        }
-        common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id));
-    } catch(err) {
-        var issue = 'asset uninstallation failed\n'+err.stack+'\n';
-        try {
-            // adding assets back
-            for(var i = 0, j = uninstalledAssets.length ; i < j ; i++) {
-               var src = uninstalledAssets[i].attrib['src'],
-                   target = uninstalledAssets[i].attrib['target'];
-               common.copyFile(plugin_dir, src, www_dir, target);
-            }
-            issue += 'but successfully reverted\n';
-        } catch(err2) {
-            issue += 'and reversion failed :(\n' + err2.stack;
-        }
-        var error = new Error(issue);
-        if (callback) callback(error);
-        else throw error;
+    var assets = plugin_et.findall('./asset');
+    if (platformTag) {
+        var sourceFiles = platformTag.findall('./source-file'),
+            headerFiles = platformTag.findall('./header-file'),
+            resourceFiles = platformTag.findall('./resource-file'),
+            frameworks = platformTag.findall('./framework');
+        assets = assets.concat(platformTag.findall('./asset'));
+
+        // queue up native stuff
+        sourceFiles && sourceFiles.forEach(function(source) {
+            action_stack.push(action_stack.createAction(handler["source-file"].uninstall, [source, project_dir], handler["source-file"].install, [source, plugin_dir, project_dir]));
+        });
+
+        headerFiles && headerFiles.forEach(function(header) {
+            action_stack.push(action_stack.createAction(handler["header-file"].uninstall, [header, project_dir], handler["header-file"].install, [header, plugin_dir, project_dir]));
+        });
+
+        resourceFiles && resourceFiles.forEach(function(resource) {
+            action_stack.push(action_stack.createAction(handler["resource-file"].uninstall, [resource, project_dir], handler["resource-file"].install, [resource, plugin_dir, project_dir]));
+        });
+
+        frameworks && frameworks.forEach(function(framework) {
+            action_stack.push(action_stack.createAction(handler["framework"].uninstall, [framework, project_dir], handler["framework"].install, [framework, plugin_dir, project_dir]));
+        });
     }
-    
-    txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks);
-    
-    // pass platform-specific transactions into uninstall
-    handler.uninstall(txs, plugin_id, project_dir, plugin_dir, function(err) {
+
+    // queue up asset installation
+    var common = require('./platforms/common');
+    assets && assets.forEach(function(asset) {
+        action_stack.push(action_stack.createAction(common.asset.uninstall, [asset, www_dir, plugin_id], common.asset.install, [asset, plugin_dir, www_dir]));
+    });
+
+    // run through the action stack
+    action_stack.process(platform, project_dir, function(err) {
         if (err) {
-            // FAIL
-            var issue = '';
-            try {
-                for(var i = 0, j = uninstalledAssets.length ; i < j ; i++) {
-                    var src = uninstalledAssets[i].attrib['src'],
-                           target = uninstalledAssets[i].attrib['target'];
-                    common.copyFile(plugin_dir, src, www_dir, target);
-                }
-            } catch(err2) {
-                issue += 'Could not revert assets' + err2.stack + '\n';
-            }
-            if (err. transactions) {
-                handler.install(err.transactions.executed, plugin_id, project_dir, plugin_dir, cli_variables, function(superr) {
-                    if (superr) {
-                        // Even reversion failed. super fail.
-                        issue += 'Uninstall failed, then reversion of uninstallation failed. Sorry :(. Uninstalation issue: ' + err.stack + ', reversion issue: ' + superr.stack;
-                    } else {
-                        issue += 'Uninstall failed, plugin reversion successful so you should be good to go. Uninstallation issue: ' + err.stack;
-                    }
-                    var error = new Error(issue);
-                    if (callback) callback(error);
-                    else throw error;
-                });
-            } else {
-                if (callback) callback(err);
-                else throw err;
-            }
+            console.error(err.message, err.stack);
+            console.error('Plugin uninstallation failed :(');
         } else {
             // WIN!
             // queue up the plugin so prepare can remove the config changes
-            config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform);
+            config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform, is_top_level);
             // call prepare after a successful uninstall
             require('./../plugman').prepare(project_dir, platform, plugins_dir);
             if (callback) callback();

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/util/action-stack.js
----------------------------------------------------------------------
diff --git a/src/util/action-stack.js b/src/util/action-stack.js
index d9ef10e..a76f33c 100644
--- a/src/util/action-stack.js
+++ b/src/util/action-stack.js
@@ -34,7 +34,7 @@ module.exports = {
                 handler.apply(null, action_params);
             } catch(e) {
                 var incomplete = stack.unshift(action);
-                var issue = 'Install failed!\n';
+                var issue = 'Uh oh!\n';
                 // revert completed tasks
                 while(completed.length) {
                     var undo = completed.shift();

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/util/config-changes.js
----------------------------------------------------------------------
diff --git a/src/util/config-changes.js b/src/util/config-changes.js
index 4ff30e9..83eefb8 100644
--- a/src/util/config-changes.js
+++ b/src/util/config-changes.js
@@ -32,19 +32,19 @@ function checkPlatform(platform) {
 }
 
 module.exports = {
-    add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars) {
+    add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars, is_top_level) {
         checkPlatform(platform);
 
         var config = module.exports.get_platform_json(plugins_dir, platform);
-        config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars});
+        config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars, 'topLevel':is_top_level});
         module.exports.save_platform_json(config, plugins_dir, platform);
     },
-    add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform) {
+    add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, is_top_level) {
         checkPlatform(platform);
 
         var plugin_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(plugins_dir, plugin, 'plugin.xml'), 'utf-8')));
         var config = module.exports.get_platform_json(plugins_dir, platform);
-        config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id']});
+        config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id'], 'topLevel':is_top_level});
         module.exports.save_platform_json(config, plugins_dir, platform);
     },
     get_platform_json:function(plugins_dir, platform) {
@@ -57,7 +57,8 @@ module.exports = {
             var config = {
                 prepare_queue:{installed:[], uninstalled:[]},
                 config_munge:{},
-                installed_plugins:{}
+                installed_plugins:{},
+                dependent_plugins:{}
             };
             fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8');
             return config;
@@ -139,7 +140,7 @@ module.exports = {
         platform_config.prepare_queue.uninstalled.forEach(function(u) {
             var plugin_dir = path.join(plugins_dir, u.plugin);
             var plugin_id = u.id;
-            var plugin_vars = platform_config.installed_plugins[plugin_id];
+            var plugin_vars = (u.topLevel ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]);
 
             // get config munge, aka how did this plugin change various config files
             var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, project_dir, plugin_vars);
@@ -210,7 +211,11 @@ module.exports = {
             platform_config.config_munge = global_munge;
 
             // Remove from installed_plugins
-            delete platform_config.installed_plugins[plugin_id]
+            if (u.topLevel) {
+                delete platform_config.installed_plugins[plugin_id]
+            } else {
+                delete platform_config.dependent_plugins[plugin_id]
+            }
         });
 
         // Empty out uninstalled queue.
@@ -292,8 +297,12 @@ module.exports = {
             });
             platform_config.config_munge = global_munge;
 
-            // Move to installed_plugins
-            platform_config.installed_plugins[plugin_id] = plugin_vars || {};
+            // Move to installed_plugins if it is a top-level plugin
+            if (u.topLevel) {
+                platform_config.installed_plugins[plugin_id] = plugin_vars || {};
+            } else {
+                platform_config.dependent_plugins[plugin_id] = plugin_vars || {};
+            }
         });
 
         // Empty out installed queue.

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/be96c196/src/util/dependencies.js
----------------------------------------------------------------------
diff --git a/src/util/dependencies.js b/src/util/dependencies.js
new file mode 100644
index 0000000..ce77dbf
--- /dev/null
+++ b/src/util/dependencies.js
@@ -0,0 +1,34 @@
+var dep_graph = require('dep-graph'),
+    path = require('path'),
+    config_changes = require('./config-changes'),
+    xml_helpers = require('./xml-helpers'),
+    underscore= require('underscore');
+
+module.exports = {
+    generate_dependency_info:function(plugins_dir, platform) {
+        var json = config_changes.get_platform_json(plugins_dir, platform);
+        var tlps = [];
+        var graph = new dep_graph();
+        Object.keys(json.installed_plugins).forEach(function(tlp) {
+            tlps.push(tlp);
+            var xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, tlp, 'plugin.xml'));
+            var deps = xml.findall('dependency');
+            deps && deps.forEach(function(dep) {
+                var id = dep.attrib.id;
+                graph.add(tlp, id);
+            });
+        });
+        Object.keys(json.dependent_plugins).forEach(function(plug) {
+            var xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, plug, 'plugin.xml'));
+            var deps = xml.findall('dependency');
+            deps && deps.forEach(function(dep) {
+                var id = dep.attrib.id;
+                graph.add(plug, id);
+            });
+        });
+        return {
+            graph:graph,
+            top_level_plugins:tlps
+        };
+    }
+};