Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2C673D685 for ; Tue, 14 May 2013 19:46:38 +0000 (UTC) Received: (qmail 21561 invoked by uid 500); 14 May 2013 19:46:38 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 21553 invoked by uid 500); 14 May 2013 19:46:38 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 21544 invoked by uid 99); 14 May 2013 19:46:37 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 14 May 2013 19:46:37 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 81DB989E8; Tue, 14 May 2013 19:46:37 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: filmaj@apache.org To: commits@cordova.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: git commit: giant refactor for dependencies, part one: changing the way install works. Date: Tue, 14 May 2013 19:46:37 +0000 (UTC) Updated Branches: refs/heads/dependencies 14eed99e0 -> 7ea9eda9e giant refactor for dependencies, part one: changing the way install works. Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/7ea9eda9 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/7ea9eda9 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/7ea9eda9 Branch: refs/heads/dependencies Commit: 7ea9eda9e427680dd4081da796f7b74211c9f5e5 Parents: 14eed99 Author: Fil Maj Authored: Tue May 14 12:45:56 2013 -0700 Committer: Fil Maj Committed: Tue May 14 12:46:25 2013 -0700 ---------------------------------------------------------------------- main.js | 2 +- package.json | 3 +- src/install.js | 183 +++++++++++++---------------- src/platforms/android.js | 65 ++--------- src/platforms/blackberry.js | 55 ++------- src/platforms/common.js | 13 ++ src/platforms/ios.js | 235 ++++++++++++++++++-------------------- src/util/action-stack.js | 50 ++++++++ src/util/dependencies.js | 56 --------- src/util/plugins.js | 2 + 10 files changed, 283 insertions(+), 381 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/main.js ---------------------------------------------------------------------- diff --git a/main.js b/main.js index 8913d02..ca2e000 100755 --- a/main.js +++ b/main.js @@ -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); } function printUsage() { http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/package.json ---------------------------------------------------------------------- diff --git a/package.json b/package.json index 2dfe157..6380287 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "plist": "0.4.x", "bplist-parser": "0.0.x", "shelljs": "0.1.x", - "osenv": "0.0.x" + "osenv": "0.0.x", + "ncallbacks":"1.1.0" }, "devDependencies": { "jasmine-node": "1.7.0" http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/install.js ---------------------------------------------------------------------- diff --git a/src/install.js b/src/install.js index 37d789c..f73f5f3 100644 --- a/src/install.js +++ b/src/install.js @@ -1,12 +1,12 @@ var path = require('path'), fs = require('fs'), et = require('elementtree'), + n = require('ncallbacks'), config_changes = require('./util/config-changes'), - dependencies = require('./util/dependencies'), + action_stack = require('./util/action-stack'), platform_modules = require('./platforms'); -// TODO: is name necessary as a param ehre? -module.exports = function installPlugin(platform, project_dir, name, plugins_dir, cli_variables, www_dir, callback) { +module.exports = function installPlugin(platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, callback) { if (!platform_modules[platform]) { var err = new Error(platform + " not supported."); if (callback) { @@ -16,12 +16,12 @@ module.exports = function installPlugin(platform, project_dir, name, plugins_dir else throw err; } - var plugin_dir = path.join(plugins_dir, name); + var plugin_dir = path.join(plugins_dir, id); // Check that the plugin has already been fetched. if (!fs.existsSync(plugin_dir)) { // if plugin doesnt exist, use fetch to get it. - require('../plugman').fetch(name, plugins_dir, false, '.', function(err, plugin_dir) { + require('../plugman').fetch(id, plugins_dir, false, subdir, function(err, plugin_dir) { if (err) { callback(err); } else { @@ -41,6 +41,21 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable , filtered_variables = {}; var name = plugin_et.findall('name').text; var plugin_id = plugin_et._root.attrib['id']; + + // 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; + Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) { + if (installed_plugin_id == plugin_id) { + is_fully_installed = true; + } + }); + if (is_fully_installed) { + console.log('Plugin "' + plugin_id + '" already installed. Carry on.'); + if (callback) callback(); + return; + } // checking preferences, if certain variables are not provided, we should throw. prefs = plugin_et.findall('./preference') || []; @@ -60,115 +75,79 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable return; } - // check if platform has plugin fully installed or queued already. - var platform_config = config_changes.get_platform_json(plugins_dir, platform); - var plugin_basename = path.basename(plugin_dir); - if (platform_config.prepare_queue.installed.indexOf(plugin_basename) > -1) { - var err = new Error('plugin "' + plugin_basename + '" is already installed (but needs to be prepared)'); - if (callback) callback(err); - else throw err; - return; - } - var is_fully_installed = false; - Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) { - if (installed_plugin_id == plugin_id) { - is_fully_installed = true; - } - }); - if (is_fully_installed) { - var err = new Error('plugin "' + plugin_basename + '" (id: '+plugin_id+') is already installed'); - if (callback) callback(err); - else throw err; - return; + // Check for dependencies, (co)recurse to install each one + 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); + }); + dependencies.forEach(function(dep) { + var dep_plugin_id = dep.attrib.id; + var dep_subdir = dep.attrib.subdir; + var dep_url = dep.attrib.url; + if (dep_subdir) { + dep_subdir = path.join.apply(null, dep_subdir.split('/')); + } + + 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); + } 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); + } + }); + } else { + handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, callback); } +} - // We need to install this plugin, so install its dependencies first. - dependencies.installAll(platform, project_dir, path.basename(plugin_dir), plugins_dir, cli_variables, www_dir, callback); +function handleInstall(plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, callback) { + var handler = platform_modules[platform]; + www_dir = www_dir || handler.www_dir(project_dir); - // TODO: if plugin does not have platform tag but has platform-agnostic config changes, should we queue it up? 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! - finalizeInstall(project_dir, plugins_dir, platform, plugin_basename, filtered_variables, callback); - return; - } + 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')); - // 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'); + // queue up native stuff + sourceFiles && sourceFiles.forEach(function(source) { + action_stack.push(action_stack.createAction(handler["source-file"].install, [source, plugin_dir, project_dir], handler["source-file"].uninstall, [source, project_dir])); + }); - assets = assets.concat(plugin_et.findall('./asset')); + headerFiles && headerFiles.forEach(function(header) { + action_stack.push(action_stack.createAction(handler["header-file"].install, [header, plugin_dir, project_dir], handler["header-file"].uninstall, [header, project_dir])); + }); - // asset installation - var installedAssets = []; - var common = require('./platforms/common'); - www_dir = www_dir || handler.www_dir(project_dir); - try { - for(var i = 0, j = assets.length ; i < j ; i++) { - var src = assets[i].attrib['src'], - target = assets[i].attrib['target']; - common.copyFile(plugin_dir, src, www_dir, target); - installedAssets.push(assets[i]); - } - } catch(err) { - var issue = 'asset installation failed\n'+err.stack+'\n'; - try { - // removing assets and reverting install - for(var i = 0, j = installedAssets.length ; i < j ; i++) { - common.removeFile(www_dir, installedAssets[i].attrib.target); - } - common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); - 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; + resourceFiles && resourceFiles.forEach(function(resource) { + action_stack.push(action_stack.createAction(handler["resource-file"].install, [resource, plugin_dir, project_dir], handler["resource-file"].uninstall, [resource, project_dir])); + }); + + frameworks && frameworks.forEach(function(framework) { + action_stack.push(action_stack.createAction(handler["framework"].install, [framework, plugin_dir, project_dir], handler["framework"].uninstall, [framework, project_dir])); + }); } - txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks); - // pass platform-specific transactions into install - handler.install(txs, plugin_id, project_dir, plugin_dir, filtered_variables, function(err) { + + // queue up asset installation + var common = require('./platforms/common'); + assets && assets.forEach(function(asset) { + action_stack.push(action_stack.createAction(common.asset.install, [asset, plugin_dir, www_dir], common.asset.uninstall, [asset, www_dir, plugin_id])); + }); + + // run through the action stack + action_stack.process(function(err) { if (err) { - // FAIL - var issue = ''; - try { - for(var i = 0, j = installedAssets.length ; i < j ; i++) { - common.removeFile(www_dir, installedAssets[i].attrib.target); - } - common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); - } catch(err2) { - issue += 'Could not revert assets' + err2.stack + '\n'; - } - if (err.transactions) { - handler.uninstall(err.transactions.executed, plugin_id, project_dir, plugin_dir, function(superr) { - - if (superr) { - // Even reversion failed. super fail. - issue += 'Install failed, then reversion of installation failed. Sorry :(. Instalation issue: ' + err.stack + ', reversion issue: ' + superr.stack; - } else { - issue += 'Install failed, plugin reversion successful so you should be good to go. Installation 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 installation failed :('); } else { - // WIN! // Log out plugin INFO element contents in case additional install steps are necessary - var info = platformTag.findall('./info'); + var info = (platformTag ? platformTag.findall('./info') : ''); if(info.length) { console.log(info[0].text); } http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/platforms/android.js ---------------------------------------------------------------------- diff --git a/src/platforms/android.js b/src/platforms/android.js index cf62f93..cda9e72 100644 --- a/src/platforms/android.js +++ b/src/platforms/android.js @@ -19,19 +19,10 @@ var fs = require('fs') // use existsSync in 0.6.x , path = require('path') - , shell = require('shelljs') , common = require('./common') - , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes')) - , plugins_module = require(path.join(__dirname, '..', 'util', 'plugins')) , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')); module.exports = { - install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { - handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); - }, - uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { - handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); - }, www_dir:function(project_dir) { return path.join(project_dir, 'assets', 'www'); }, @@ -42,51 +33,15 @@ module.exports = { var mDoc = xml_helpers.parseElementtreeSync(path.join(project_dir, 'AndroidManifest.xml')); return mDoc._root.attrib['package']; - } -}; - -function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { - variables = variables || {}; - - // TODO: adding access tags? - // TODO: move this to prepare? - /* - var root = et.Element("config-file"); - root.attrib['parent'] = '.'; - plugin_et.findall('./access').forEach(function (tag) { - root.append(tag); - }); - */ - var completed = []; - while(txs.length) { - var mod = txs.shift(); - try { - switch(mod.tag.toLowerCase()) { - case 'source-file': - var destFile = path.join(mod.attrib['target-dir'], path.basename(mod.attrib['src'])); - - if (action == 'install') { - common.copyFile(plugin_dir, mod.attrib['src'], project_dir, destFile); - } else { - common.deleteJava(project_dir, destFile); - } - break; - default: - throw new Error('Unrecognized plugin.xml element/action in android installer: ' + mod.tag); - break; - } - } catch(e) { - // propagate error up and provide completed tx log - e.transactions = { - executed:completed, - incomplete:txs.unshift(mod) - }; - if (callback) callback(e); - else throw e; - return; + }, + "source-file":{ + install:function(source_el, plugin_dir, project_dir) { + var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); + common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest); + }, + uninstall:function(source_el, project_dir) { + var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); + common.deleteJava(project_dir, dest); } - completed.push(mod); } - - if (callback) callback(); -} +}; http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/platforms/blackberry.js ---------------------------------------------------------------------- diff --git a/src/platforms/blackberry.js b/src/platforms/blackberry.js index 9643a77..353ef92 100644 --- a/src/platforms/blackberry.js +++ b/src/platforms/blackberry.js @@ -19,59 +19,26 @@ var fs = require('fs') // use existsSync in 0.6.x , path = require('path') - , shell = require('shelljs') - , et = require('elementtree') - , getConfigChanges = require('../util/config-changes') , common = require('./common') , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')); module.exports = { - install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { - handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); - }, - uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { - handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); - }, www_dir:function(project_dir) { return path.join(project_dir, 'www'); }, package_name:function(project_dir) { var config_path = path.join(module.exports.www_dir(project_dir), 'config.xml'); - var widget_doc = new et.ElementTree(et.XML(fs.readFileSync(config_path, 'utf-8'))); + var widget_doc = xml_helpers.parseElementtreeSync(config_path); return widget_doc._root.attrib['id']; - } -}; - -function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { - var completed = []; - while(txs.length) { - var mod = txs.shift(); - try { - switch(mod.tag.toLowerCase()) { - case 'source-file': - var destFile = path.join(mod.attrib['target-dir'], path.basename(mod.attrib['src'])); - - if (action == 'install') { - common.copyFile(plugin_dir, mod.attrib['src'], project_dir, destFile); - } else { - common.deleteJava(project_dir, destFile); - } - break; - default: - throw new Error('Unrecognized plugin.xml element/action in blackberry installer: ' + mod.tag); - break; - } - } catch(e) { - // propagate error up and provide completed tx log - e.transactions = { - executed:completed, - incomplete:txs.unshift(mod) - }; - if (callback) callback(e); - else throw e; - return; + }, + "source-file":{ + install:function(source_el, plugin_dir, project_dir) { + var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); + common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest); + }, + uninstall:function(source_el, project_dir) { + var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); + common.deleteJava(project_dir, dest); } - completed.push(mod); } - if (callback) callback(); -} +}; http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/platforms/common.js ---------------------------------------------------------------------- diff --git a/src/platforms/common.js b/src/platforms/common.js index 7d294b9..d4ee21d 100644 --- a/src/platforms/common.js +++ b/src/platforms/common.js @@ -55,5 +55,18 @@ module.exports = { break; } } + }, + // handle elements + asset:{ + install:function(asset_el, plugin_dir, www_dir) { + var src = asset_el.attrib.src; + var target = asset_el.attrib.target; + module.exports.copyFile(plugin_dir, src, www_dir, target); + }, + uninstall:function(asset_el, www_dir, plugin_id) { + var target = asset_el.attrib.target; + module.exports.removeFile(www_dir, target); + module.exports.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); + } } }; http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/platforms/ios.js ---------------------------------------------------------------------- diff --git a/src/platforms/ios.js b/src/platforms/ios.js index 4c012d7..c0187c5 100644 --- a/src/platforms/ios.js +++ b/src/platforms/ios.js @@ -20,44 +20,130 @@ var path = require('path') , fs = require('../util/fs') // use existsSync in 0.6.x , glob = require('glob') - , et = require('elementtree') , xcode = require('xcode') , plist = require('plist') - , bplist = require('bplist-parser') - , shell = require('shelljs') - , common = require('./common') - , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')) - , searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace')) - , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes')); + , shell = require('shelljs'); module.exports = { - install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { - handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); - }, - uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { - handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); - }, www_dir:function(project_dir) { return path.join(project_dir, 'www'); }, package_name:function(project_dir) { var plist_file = glob.sync(path.join(project_dir, '**', '*-Info.plist'))[0]; return plist.parseFileSync(plist_file).CFBundleIdentifier; + }, + "source-file":{ + install:function(source_el, plugin_dir, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = source_el.attrib['src']; + var srcFile = path.resolve(plugin_dir, src); + var targetDir = path.resolve(project.plugins_dir, getRelativeDir(source_el)); + var destFile = path.resolve(targetDir, path.basename(src)); + + if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); + if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); + project.xcode.addSourceFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); + shell.mkdir('-p', targetDir); + shell.cp(srcFile, destFile); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + }, + uninstall:function(source_el, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = source_el.attrib['src']; + var targetDir = path.resolve(project.plugins_dir, getRelativeDir(source_el)); + var destFile = path.resolve(targetDir, path.basename(src)); + + project.xcode.removeSourceFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); + shell.rm('-rf', destFile); + + if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ + shell.rm('-rf', targetDir); + } + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + } + }, + "header-file":{ + install:function(header_el, plugin_dir, project_dir) { + var project = getIOSProjectFiles(project_dir); + 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)); + if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); + if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); + project.xcode.addHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); + shell.mkdir('-p', targetDir); + shell.cp(srcFile, destFile); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + }, + uninstall:function(header_el, project_dir) { + var project = getIOSProjectFiles(project_dir); + 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))); + shell.rm('-rf', destFile); + if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ + shell.rm('-rf', targetDir); + } + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + } + }, + "resource-file":{ + install:function(resource_el, plugin_dir, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = resource_el.attrib['src'], + srcFile = path.resolve(plugin_dir, src), + destFile = path.resolve(project.resources_dir, path.basename(src)); + if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); + if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); + project.xcode.addResourceFile(path.join('Resources', path.basename(src))); + shell.cp('-R', srcFile, resourcesDir); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + }, + uninstall:function(resource_el, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = resource_el.attrib['src'], + destFile = path.resolve(project.resources_dir, path.basename(src)); + project.xcode.removeResourceFile(path.join('Resources', path.basename(src))); + shell.rm('-rf', destFile); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + } + }, + "framework":{ + install:function(framework_el, plugin_dir, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = framework_el.attrib['src'], + weak = framework_el.attrib['weak']; + var opt = { weak: (weak == undefined || weak == null || weak != 'true' ? false : true ) }; + project.xcode.addFramework(src, opt); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + }, + uninstall:function(framework_el, project_dir) { + var project = getIOSProjectFiles(project_dir); + var src = framework_el.attrib['src']; + project.xcode.removeFramework(src); + // write out xcodeproj file + fs.writeFileSync(project.pbx, project.xcode.writeSync()); + } } }; -function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { - variables = variables || {}; - +function getIOSProjectFiles(project_dir) { // grab and parse pbxproj // we don't want CordovaLib's xcode project var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj')); if (project_files.length === 0) { - var err = new Error("does not appear to be an xcode project (no xcode project file)"); - if (callback) callback(err); - else throw err; - return; + throw new Error("does not appear to be an xcode project (no xcode project file)"); } var pbxPath = project_files[0]; var xcodeproj = xcode.project(pbxPath); @@ -74,10 +160,7 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables }); if (config_files.length === 0) { - var err = new Error("could not find PhoneGap/Cordova plist file, or config.xml file."); - if (callback) callback(err); - else throw err; - return; + throw new Error("could not find PhoneGap/Cordova plist file, or config.xml file."); } var config_file = config_files[0]; @@ -92,95 +175,12 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables // for certain config changes, we need to know if plugins-plist elements are present var plistEle = txs.filter(function(t) { return t.tag.toLowerCase() == 'plugins-plist'; })[0]; - var completed = []; - while(txs.length) { - var mod = txs.shift(); - try { - switch(mod.tag.toLowerCase()) { - case 'source-file': - var src = mod.attrib['src']; - var srcFile = path.resolve(plugin_dir, src); - var targetDir = path.resolve(pluginsDir, getRelativeDir(mod)); - var destFile = path.resolve(targetDir, path.basename(src)); - if (action == 'install') { - if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); - if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); - xcodeproj.addSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile))); - shell.mkdir('-p', targetDir); - shell.cp(srcFile, destFile); - } else { - xcodeproj.removeSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile))); - shell.rm('-rf', destFile); - - if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ - shell.rm('-rf', targetDir); - } - } - break; - case 'header-file': - var src = mod.attrib['src']; - var srcFile = path.resolve(plugin_dir, src); - var targetDir = path.resolve(pluginsDir, getRelativeDir(mod)); - var destFile = path.resolve(targetDir, path.basename(src)); - if (action == 'install') { - if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); - if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); - xcodeproj.addHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile))); - shell.mkdir('-p', targetDir); - shell.cp(srcFile, destFile); - } else { - // TODO: doesnt preserve-dirs affect what the originally-added path to xcodeproj (see above) affect how we should call remove too? - xcodeproj.removeHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile))); - shell.rm('-rf', destFile); - // TODO: again.. is this right? same as source-file - shell.rm('-rf', targetDir); - } - break; - case 'resource-file': - var src = mod.attrib['src'], - srcFile = path.resolve(plugin_dir, src), - destFile = path.resolve(resourcesDir, path.basename(src)); - - if (action == 'install') { - if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios '); - if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); - xcodeproj.addResourceFile(path.join('Resources', path.basename(src))); - shell.cp('-R', srcFile, resourcesDir); - } else { - xcodeproj.removeResourceFile(path.join('Resources', path.basename(src))); - shell.rm('-rf', destFile); - } - break; - case 'framework': - var src = mod.attrib['src'], - weak = mod.attrib['weak']; - if (action == 'install') { - var opt = { weak: (weak == undefined || weak == null || weak != 'true' ? false : true ) }; - xcodeproj.addFramework(src, opt); - } else { - xcodeproj.removeFramework(src); - } - break; - default: - throw new Error('Unrecognized plugin.xml element/action in ios installer: ' + mod.tag); - break; - } - } catch(e) { - // propagate error up and provide completed tx log - e.transactions = { - executed:completed, - incomplete:txs.unshift(mod) - }; - if (callback) callback(e); - else throw e; - return; - } - completed.push(mod); - } - // write out xcodeproj file - fs.writeFileSync(pbxPath, xcodeproj.writeSync()); - - if (callback) callback(); + return { + plugins_dir:pluginsDir, + resources_dir:resourcesDir, + xcode:xcodeproj, + pbx:pbxPath + }; } function getRelativeDir(file) { @@ -191,12 +191,3 @@ function getRelativeDir(file) { return ''; } } - -// determine if a plist file is binary -function isBinaryPlist(filename) { - // I wish there was a synchronous way to read only the first 6 bytes of a - // file. This is wasteful :/ - var buf = '' + fs.readFileSync(filename, 'utf8'); - // binary plists start with a magic header, "bplist" - return buf.substring(0, 6) === 'bplist'; -} http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/util/action-stack.js ---------------------------------------------------------------------- diff --git a/src/util/action-stack.js b/src/util/action-stack.js new file mode 100644 index 0000000..849bb54 --- /dev/null +++ b/src/util/action-stack.js @@ -0,0 +1,50 @@ +var stack = []; +var completed = []; + +module.exports = { + createAction:function(handler, action_params, reverter, revert_params) { + return { + handler:{ + run:handler, + params:action_params + }, + reverter:{ + run:reverter, + params:revert_params + } + }; + }, + push:function(tx) { + stack.push(tx); + }, + process:function(callback) { + while(stack.length) { + var action = stack.shift(); + var handler = action.handler.run; + var action_params = action.handler.params; + try { + handler.apply(null, action_params); + } catch(e) { + var incomplete = stack.unshift(action); + var issue = 'Install failed!\n'; + // revert completed tasks + while(completed.length) { + var undo = completed.shift(); + var revert = undo.reverter.run; + var revert_params = undo.reverter.params; + try { + revert.apply(null, revert_params); + } catch(err) { + issue += 'A reversion action failed: ' + err.message + '\n'; + } + } + e.message = issue + e.message; + if (callback) callback(e); + else throw e; + return; + } + completed.push(action); + } + if (callback) callback(); + } +}; http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/util/dependencies.js ---------------------------------------------------------------------- diff --git a/src/util/dependencies.js b/src/util/dependencies.js deleted file mode 100644 index 9f7a594..0000000 --- a/src/util/dependencies.js +++ /dev/null @@ -1,56 +0,0 @@ -var install = require('../install'), - fetch = require('../fetch'), - path = require('path'), - fs = require('fs'), - xml_helpers = require('./xml-helpers'); - -exports.installAll = function(platform, project_dir, name, plugins_dir, cli_variables, www_dir, callback) { - // It should have been fetched already, so read the plugin.xml from plugin_dir - var plugin_dir = path.join(plugins_dir, name); - var xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml')); - - var dependencies = xml.findall('dependency'); - - if (!dependencies || dependencies.length == 0) { - return; - } - - function waitForAll(n) { - var count = n; - var errs = []; - return function(err) { - count--; - if (err) { - throw err; - } - if (count == 0) { - callback(); - } - }; - } - - var dependencyCallback = waitForAll(dependencies.length); - - dependencies && dependencies.forEach(function(dep) { - function doInstall(plugin_dir) { - // Call installation for this plugin after it gets fetched. - install(platform, project_dir, path.basename(plugin_dir), plugins_dir, cli_variables, www_dir, dependencyCallback); - } - - // Check if this dependency is already there. - if (fs.existsSync(path.join(plugins_dir, dep.attrib.id))) { - console.log('Plugin ' + dep.attrib.id + ' already fetched, using that version.'); - doInstall(path.join(plugins_dir, dep.attrib.id)); - } else { - // Fetch it. - var subdir = dep.attrib.subdir; - if (subdir) { - subdir = path.join.apply(null, dep.attrib.subdir.split('/')); - } - - console.log('Fetching dependency ' + dep.attrib.id); - fetch(dep.attrib.url, plugins_dir, false /* no link */, subdir, doInstall); - } - }); -}; - http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/7ea9eda9/src/util/plugins.js ---------------------------------------------------------------------- diff --git a/src/util/plugins.js b/src/util/plugins.js index ff9cace..d5141cb 100644 --- a/src/util/plugins.js +++ b/src/util/plugins.js @@ -53,6 +53,8 @@ module.exports = { var xml = xml_helpers.parseElementtreeSync(xml_file); var plugin_id = xml.getroot().attrib.id; + // TODO: what if a plugin dependended on different subdirectories of the same plugin? this would fail. + // should probably copy over entire plugin git repo contents into plugins_dir and handle subdir seperately during install. var plugin_dir = path.join(plugins_dir, plugin_id); shell.cp('-R', path.join(tmp_dir, '*'), plugin_dir);