You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by br...@apache.org on 2013/03/21 23:16:34 UTC

git commit: --prepare with ported JS installation, cache plugins locally

Updated Branches:
  refs/heads/future c770f5250 -> c130382d2


--prepare with ported JS installation, cache plugins locally

NB: --plugins_dir argument and the --prepare command are completely
untested. I'm just committing the skeleton of this change to unblock
others.


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

Branch: refs/heads/future
Commit: c130382d2f00accb92e3b07c68de3c83d13727e1
Parents: c770f52
Author: Braden Shepherdson <br...@gmail.com>
Authored: Tue Mar 19 14:21:16 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu Mar 21 18:16:07 2013 -0400

----------------------------------------------------------------------
 FUTURE.md             |    4 +-
 plugman.js            |   37 +++++++++++-
 util/plugin_loader.js |  141 ++++++++++++++++++++++++++++++++++++++++++++
 util/plugins.js       |   21 +++----
 4 files changed, 189 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/c130382d/FUTURE.md
----------------------------------------------------------------------
diff --git a/FUTURE.md b/FUTURE.md
index 6ff6884..41c2c62 100644
--- a/FUTURE.md
+++ b/FUTURE.md
@@ -35,8 +35,8 @@ Care is required here not to remove permissions that are still needed by other p
 
 ### `--prepare`
 
-Takes over part of cordova-cli's `prepare` command. Copies all plugins' Javascript files (more precisely, those specified in `<js-module>` tags rather than `<asset>` tags) into `www/plugins/com.plugin.id/whatever/path/file.js` and constructs the `plugins.json` file.
+Takes over part of cordova-cli's `prepare` command. Copies all plugins' Javascript files (more precisely, those specified in `<js-module>` tags rather than `<asset>` tags) into `www/plugins/com.plugin.id/whatever/path/file.js` and constructs the `cordova_plugins.json` file.
 
-`cordova.js` in this new model will have code that reads this `plugins.json` file via XHR, loads the JS files for the plugins, and does their clobbers and merges.
+`cordova.js` in this new model will have code that reads this `cordova_plugins.json` file via XHR, loads the JS files for the plugins, and does their clobbers and merges.
 
 This is something of a change from the current cordova-cli method, but necessary because we won't be working with a fresh `cordova.js` file on each run anymore.

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/c130382d/plugman.js
----------------------------------------------------------------------
diff --git a/plugman.js b/plugman.js
index 9ddcd94..0ecad95 100755
--- a/plugman.js
+++ b/plugman.js
@@ -26,6 +26,8 @@ var fs = require('fs')
   , et = require('elementtree')
   , nopt = require('nopt')
   , plugins = require('./util/plugins')
+  , shell = require('shelljs')
+  , plugin_loader = require('./util/plugin_loader')
   , platform_modules = {
         'android': require('./platforms/android'),
         'ios': require('./platforms/ios'),
@@ -40,10 +42,23 @@ var known_opts = { 'platform' : [ 'ios', 'android', 'bb10' ,'www' ]
             , 'list' : Boolean
             , 'v' : Boolean
             , 'debug' : Boolean
+            , 'prepare' : Boolean
+            , 'plugins': path
+            , 'www': path
             };
 
 var cli_opts = nopt(known_opts);
 
+// Default the plugins_dir to './cordova/plugins'.
+var plugins_dir;
+
+// Without these arguments, the commands will fail and print the usage anyway.
+if (cli_opts.plugins_dir || cli_opts.project) {
+    plugins_dir = typeof cli_opts.plugins_dir == 'string' ?
+        cli_opts.plugins_dir :
+        path.join(cli_opts.project, 'cordova', 'plugins');
+}
+
 // only dump stack traces in debug mode, otherwise show error message and exit
 if (!cli_opts.debug) {
     // provide clean output on exceptions rather than dumping a stack trace
@@ -63,6 +78,9 @@ else if (cli_opts.list) {
       }
     });
 }
+else if (cli_opts.prepare && cli_opts.project && cli_opts.www) {
+    util.handlePrepare(cli_opts.project, plugins_dir, cli_opts.www, cli_opts.platform);
+}
 else if (!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
     printUsage();
 }
@@ -79,6 +97,8 @@ function printUsage() {
     console.log('Add a plugin:\n\t' + package.name + ' --platform <'+ platforms +'> --project <directory> --plugin <directory|git-url|name>\n');
     console.log('Remove a plugin:\n\t' + package.name + ' --remove --platform <'+ platforms +'> --project <directory> --plugin <directory|git-url|name>\n');
     console.log('List plugins:\n\t' + package.name + ' --list\n');
+    console.log('Prepare project:\n\t' + package.name + ' --prepare --platform <ios|android|bb10> --project <directory> --www <directory> [--plugins_dir <directory>]');
+    console.log('\n\t--plugins_dir defaults to <project>/cordova/plugins, but can be any directory containing a subdirectory for each plugin');
 }
 
 function execAction(action, platform, project_dir, plugin_dir) {
@@ -95,9 +115,24 @@ function execAction(action, platform, project_dir, plugin_dir) {
 function handlePlugin(action, platform, project_dir, plugin_dir) {
     var plugin_xml_path, async = false;
 
+    // Ensure the containing directory exists.
+    shell.mkdir('-p', plugins_dir);
+
     // clone from git repository
     if(plugin_dir.indexOf('https://') == 0 || plugin_dir.indexOf('git://') == 0) {
-        plugin_dir = plugins.clonePluginGitRepo(plugin_dir);
+        plugin_dir = plugins.clonePluginGitRepo(plugin_dir, plugins_dir);
+    } else { // Copy from the local filesystem.
+        var lastSlash = plugin_dir.lastIndexOf(path.sep);
+        var dest = plugin_dir;
+        if (lastSlash >= 0) {
+            dest = dest.substring(lastSlash+1);
+        }
+        dest = path.join(plugins_dir, dest);
+
+        shell.rm('-rf', dest);
+        shell.cp('-R', plugin_dir, plugins_dir); // Yes, not dest.
+
+        plugin_dir = dest;
     }
 
     plugin_xml_path = path.join(plugin_dir, 'plugin.xml');

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/c130382d/util/plugin_loader.js
----------------------------------------------------------------------
diff --git a/util/plugin_loader.js b/util/plugin_loader.js
new file mode 100644
index 0000000..6d55694
--- /dev/null
+++ b/util/plugin_loader.js
@@ -0,0 +1,141 @@
+/**
+    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 path            = require('path'),
+    fs              = require('fs'),
+    shell           = require('shelljs'),
+    ls              = fs.readdirSync,
+    util            = require('util'),
+    exec            = require('child_process').exec,
+    et              = require('elementtree');
+
+
+// Called on --prepare.
+// Sets up each plugin's Javascript code to be loaded properly.
+// Expects a path to the project (platforms/android in CLI, . in plugman-only),
+// a path to where the plugins are downloaded, the www dir, and the platform ('android', 'ios', etc.).
+exports.handlePrepare = function(projectRoot, plugins_dir, wwwDir, platform) {
+    // Process:
+    // - List all plugins in plugins_dir
+    // - Load and parse their plugin.xml files.
+    // - Skip those without support for this platform. (No <platform> tags means JS-only!)
+    // - Build a list of all their js-modules, including platform-specific js-modules.
+    // - For each js-module (general first, then platform) build up an object storing the path and any clobbers, merges and runs for it.
+    // - Write this object into www/plugins.json.
+    // - Cordova.js contains code to load them at runtime from that file.
+
+    var plugins = ls(plugins_dir);
+
+    // This array holds all the metadata for each module and ends up in cordova_plugins.json
+    var moduleObjects = [];
+
+
+    plugins && plugins.forEach(function(plugin) {
+        var pluginDir = path.join(plugins_dir, plugin);
+        var xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(pluginDir, 'plugin.xml'), 'utf-8')));
+
+        var plugin_id = xml.getroot().attrib.id;
+
+        // Copy all the <asset>s into the platform's www/
+        var assets = xml.findall('./asset');
+        assets && assets.forEach(function(asset) {
+            var target = asset.attrib.target;
+            var lastSlash = target.lastIndexOf('/');
+            var dirname  = lastSlash < 0 ? ''     : target.substring(0, lastSlash);
+            var basename = lastSlash < 0 ? target : target.substring(lastSlash + 1);
+
+            var targetDir = path.join(wwwDir, dirname);
+            shell.mkdir('-p', targetDir);
+
+            var srcFile = path.join(pluginDir, asset.attrib.src);
+            var targetFile = path.join(targetDir, basename);
+
+            var cpOptions = fs.statSync(srcFile).isDirectory() ? '-Rf' : '-f';
+            shell.cp(cpOptions, [srcFile], targetFile);
+        });
+
+        // And then add the plugins dir to the platform's www.
+        var platformPluginsDir = path.join(wwwDir, 'plugins');
+        shell.mkdir('-p', platformPluginsDir);
+
+        var generalModules = xml.findall('./js-module');
+        var platformTag = xml.find(util.format('./platform[@name="%s"]', platform));
+
+        generalModules = generalModules || [];
+        var platformModules = platformTag ? platformTag.findall('./js-module') : [];
+        var allModules = generalModules.concat(platformModules);
+
+        allModules.forEach(function(module) {
+            // Copy the plugin's files into the www directory.
+            var dirname = module.attrib.src;
+            var lastSlash = dirname.lastIndexOf('/');
+            if (lastSlash >= 0) {
+                dirname = dirname.substring(0, lastSlash);
+            } else {
+                dirname = ''; // Just the file, no subdir.
+            }
+
+            var dir = path.join(platformPluginsDir, plugin_id, dirname);
+            shell.mkdir('-p', dir);
+
+            // Read in the file, prepend the cordova.define, and write it back out.
+            var moduleName = plugin_id + '.';
+            if (module.attrib.name) {
+                moduleName += module.attrib.name;
+            } else {
+                var result = module.attrib.src.match(/([^\/]+)\.js/);
+                moduleName += result[1];
+            }
+
+            var scriptContent = fs.readFileSync(path.join(pluginDir, module.attrib.src), 'utf-8');
+            scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {' + scriptContent + '});\n';
+            fs.writeFileSync(path.join(platformPluginsDir, plugin_id, module.attrib.src), scriptContent, 'utf-8');
+
+            // Prepare the object for cordova_plugins.json.
+            var obj = {
+                file: path.join('plugins', plugin_id, module.attrib.src),
+                id: moduleName
+            };
+
+            // Loop over the children of the js-module tag, collecting clobbers, merges and runs.
+            module.getchildren().forEach(function(child) {
+                if (child.tag.toLowerCase() == 'clobbers') {
+                    if (!obj.clobbers) {
+                        obj.clobbers = [];
+                    }
+                    obj.clobbers.push(child.attrib.target);
+                } else if (child.tag.toLowerCase() == 'merges') {
+                    if (!obj.merges) {
+                        obj.merges = [];
+                    }
+                    obj.merges.push(child.attrib.target);
+                } else if (child.tag.toLowerCase() == 'runs') {
+                    obj.runs = true;
+                }
+            });
+
+            // Add it to the list of module objects bound for cordova_plugins.json
+            moduleObjects.push(obj);
+        });
+    });
+
+    // Write out moduleObjects as JSON to cordova_plugins.json
+    fs.writeFileSync(path.join(wwwDir, 'cordova_plugins.json'), JSON.stringify(moduleObjects), 'utf-8');
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/c130382d/util/plugins.js
----------------------------------------------------------------------
diff --git a/util/plugins.js b/util/plugins.js
index 19e97a8..333fd92 100644
--- a/util/plugins.js
+++ b/util/plugins.js
@@ -66,13 +66,20 @@ exports.listAllPlugins = function(success, error) {
     });
 }
 
-exports.clonePluginGitRepo = function(plugin_git_url) {
+exports.clonePluginGitRepo = function(plugin_git_url, plugins_dir) {
     if(!shell.which('git')) {
         throw new Error('git command line is not installed');
     }
     // use osenv to get a temp directory in a portable way
-    plugin_dir = path.join(osenv.tmpdir(), 'plugin');
-    
+    var lastSlash = plugin_git_url.lastIndexOf('/');
+    var basename = plugin_git_url.substring(lastSlash+1);
+    var dotGitIndex = basename.lastIndexOf('.git');
+    if (dotGitIndex >= 0) {
+      basename = basename.substring(0, dotGitIndex);
+    }
+
+    var plugin_dir = path.join(plugins_dir, basename);
+
     // trash it if it already exists (something went wrong before probably)
     if(fs.existsSync(plugin_dir)) {
         shell.rm('-rf', plugin_dir);
@@ -82,14 +89,6 @@ exports.clonePluginGitRepo = function(plugin_git_url) {
         throw new Error('failed to get the plugin via git URL '+ plugin_git_url);
     }
     
-    process.on('exit', function() {
-        console.log('cleaning up...');
-        // clean up
-        if(fs.existsSync(plugin_dir)) {
-            shell.rm('-rf', plugin_dir);
-        }
-    });
-
     return plugin_dir;
 }