Updated Branches: refs/heads/future f4543544e -> 4d8f6774e
finished refactor of ios handler. install grabs plugins-plist and hands over to handlers now too. Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/4d8f6774 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/4d8f6774 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/4d8f6774 Branch: refs/heads/future Commit: 4d8f6774edb77a26756bd26159c8ad8a27b09b15 Parents: f454354 Author: Fil Maj <[email protected]> Authored: Tue Apr 23 10:40:37 2013 -0700 Committer: Fil Maj <[email protected]> Committed: Tue Apr 23 10:40:37 2013 -0700 ---------------------------------------------------------------------- src/install.js | 3 +- src/platforms/android.js | 4 +- src/platforms/ios.js | 313 +++++++++++++---------------------------- 3 files changed, 104 insertions(+), 216 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/4d8f6774/src/install.js ---------------------------------------------------------------------- diff --git a/src/install.js b/src/install.js index 7a70cfa..29cf041 100644 --- a/src/install.js +++ b/src/install.js @@ -78,9 +78,10 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable resourceFiles = platformTag.findall('./resource-file'), assets = plugin_et.findall('./asset'), frameworks = platformTag.findall('./framework'), + pluginsPlist = platformTag.findall('./plugins-plist'), configChanges = platformTag.findall('./config-file'); - txs = txs.concat(sourceFiles, libFiles, headerFiles, resourceFiles, frameworks, configChanges, assets); + txs = txs.concat(sourceFiles, libFiles, headerFiles, resourceFiles, frameworks, configChanges, assets, pluginsPlist); // pass platform-specific transactions into install handler.install(txs, plugin_id, project_dir, plugin_dir, filtered_variables, function(err) { http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/4d8f6774/src/platforms/android.js ---------------------------------------------------------------------- diff --git a/src/platforms/android.js b/src/platforms/android.js index 7bdc436..6b0ef46 100644 --- a/src/platforms/android.js +++ b/src/platforms/android.js @@ -83,11 +83,11 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables if (action == 'install') { if (!xml_helpers.graftXML(xmlDoc, children, selector)) { - throw new Error('failed to add config-file children to "' + filename + '"'); + throw new Error('failed to add config-file children to "' + selector + '" to "'+ filename + '"'); } } else { if (!xml_helpers.pruneXML(xmlDoc, children, selector)) { - throw new Error('failed to remove config-file children from "' + filename + '"'); + throw new Error('failed to remove config-file children from "' + selector + '" from "' + filename + '"'); } } http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/4d8f6774/src/platforms/ios.js ---------------------------------------------------------------------- diff --git a/src/platforms/ios.js b/src/platforms/ios.js index 8368983..62de7d0 100644 --- a/src/platforms/ios.js +++ b/src/platforms/ios.js @@ -70,13 +70,14 @@ 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."); + var err = new Error("could not find PhoneGap/Cordova plist file, or config.xml file."); if (callback) callback(err); else throw err; return; } var config_file = config_files[0]; + var base_config_path = path.basename(config_file); var xcode_dir = path.dirname(config_file); var pluginsDir = path.resolve(xcode_dir, 'Plugins'); var resourcesDir = path.resolve(xcode_dir, 'Resources'); @@ -84,6 +85,9 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables var project_plists = glob.sync(xcode_dir + '/*-Info.plist'); var projectPListPath = project_plists[0]; + // 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'; }); + var completed = []; while(txs.length) { var mod = txs.shift(); @@ -103,8 +107,7 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables shell.cp(srcFile, destFile); } else { xcodeproj.removeSourceFile(path.join('Plugins', path.basename(src))); - if(fs.existsSync(destFile)) - fs.unlinkSync(destFile); + shell.rm('-rf', destFile); // TODO: is this right, should we check if dir is empty? shell.rm('-rf', targetDir); } @@ -118,21 +121,67 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables fs.unlinkSync(path.resolve(project_dir, destFile)); } break; + case 'plugins-plist': + // Only handle this if the config file is cordova/phonegap plist. + if (path.extname(config_file) == '.plist') { + // determine if this is a binary or ascii plist and choose the parser + // TODO: this is temporary until binary support is added to node-plist + var pl = (isBinaryPlist(config_file) ? bplist : plist); + var plistObj = pl.parseFileSync(config_file); + + if (action == 'install') { + // TODO: move to prepare? + // add hosts to whitelist (ExternalHosts) in plist + /* + hosts && hosts.forEach(function(host) { + plistObj.ExternalHosts.push(host.attrib['origin']); + }); + */ + + // add plugin to plist + plistObj.Plugins[mod.attrib['key']] = mod.attrib['string']; + } else { + delete plistObj.Plugins[mod.attrib['key']]; + } + + // write out plist + fs.writeFileSync(config_file, plist.build(plistObj)); + } + break; case 'config-file': - // Only modify config files that exist. - var config_file = path.resolve(project_dir, mod.attrib['target']); - if (fs.existsSync(config_file)) { - var xmlDoc = xml_helpers.parseElementtreeSync(config_file); - var selector = mod.attrib["parent"]; - var children = mod.findall('*'); + // Only use config file appropriate for the current cordova-ios project + if (mod.attribs['target'] == base_config_path) { + // edit configuration files + var xmlDoc = xml_helpers.parseElementtreeSync(config_file), + pListOnly = plistEle; + + if (mod.find("plugin")) pListOnly = false; + + if (pListOnly) { + // if the plugin supports the old plugins-plist element only + var name = plistEle.attrib.key; + var value = plistEle.attrib.string; + var pluginsEl = xmlDoc.find('plugins'); + if ( action == 'install') { + var new_plugin = new et.Element('plugin'); + new_plugin.attrib.name = name; + new_plugin.attrib.value = value; + pluginsEl.append(new_plugin); + } else { + var culprit = pluginsEl.find("plugin[@name='"+name+"']"); + pluginsEl.remove(0, culprit); + } + } - if (action == 'install') { + var selector = mod.attrib["parent"], + children = mod.findall('*'); + if( action == 'install') { if (!xml_helpers.graftXML(xmlDoc, children, selector)) { - throw new Error('failed to add config-file children to "' + filename + '"'); + throw new Error('failed to add config-file children to "' + selector + '" to "' + config_file + '"'); } } else { if (!xml_helpers.pruneXML(xmlDoc, children, selector)) { - throw new Error('failed to remove config-file children from "' + filename + '"'); + throw new Error('failed to remove config-file children from "' + selector + '" from "' + config_path + '"'); } } @@ -148,10 +197,48 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables } 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 <header-file>'); + 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 { + xcodeproj.removeHeaderFile(path.join('Plugins', path.basename(src))); + 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 <header-file>'); + 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') { + // TODO: check for src existence/collision? + var opt = { weak: (weak && weak.toLowerCase() == 'true') }; + xcodeproj.addFramework(src, opt); + } else { + xcodeproj.removeFramework(src); + } break; default: throw new Error('Unrecognized plugin.xml element/action in android installer: ' + mod.tag); @@ -170,93 +257,16 @@ function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables completed.push(mod); } - headerFiles && headerFiles.forEach(function (headerFile) { - var src = headerFile.attrib['src'], - srcFile = path.resolve(plugin_dir, src), - targetDir = path.resolve(pluginsDir, getRelativeDir(headerFile)), - destFile = path.resolve(targetDir, path.basename(src)); - - if (action == 'install') { - xcodeproj.addHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile))); - shell.mkdir('-p', targetDir); - checkLastCommand(); - shell.cp(srcFile, destFile); - checkLastCommand(); - } else { - xcodeproj.removeHeaderFile(path.join('Plugins', path.basename(src))); - if(fs.existsSync(destFile)) - fs.unlinkSync(destFile); - shell.rm('-rf', targetDir); - checkLastCommand(); - } - }); - - resourceFiles && resourceFiles.forEach(function (resource) { - var src = resource.attrib['src'], - srcFile = path.resolve(plugin_dir, src), - destFile = path.resolve(resourcesDir, path.basename(src)); - - if (action == 'install') { - xcodeproj.addResourceFile(path.join('Resources', path.basename(src))); - var st = fs.statSync(srcFile); - shell.mkdir('-p', resourcesDir); - if (st.isDirectory()) { - shell.cp('-R', srcFile, resourcesDir); - checkLastCommand(); - } else { - shell.cp(srcFile, destFile); - checkLastCommand(); - } - } else { - xcodeproj.removeResourceFile(path.join('Resources', path.basename(src))); - shell.rm('-rf', destFile); - checkLastCommand(); - } - }); - - frameworks && frameworks.forEach(function (framework) { - var src = framework.attrib['src'], - weak = framework.attrib['weak']; - if (action == 'install') { - var opt = { weak: (weak && weak.toLowerCase() == 'true') }; - xcodeproj.addFramework(src, opt); - } else { - xcodeproj.removeFramework(src); - } - }); - // write out xcodeproj file fs.writeFileSync(pbxPath, xcodeproj.writeSync()); - // add plugin and whitelisted hosts - try { - updateConfig(action, config_file, plugin_et); - } catch(e) { - throw { - name: "ConfigurationError", - level: "ERROR", - message: "Error updating configuration: "+e.message, - } - } - if (action == 'install') { variables['PACKAGE_NAME'] = plist.parseFileSync(projectPListPath).CFBundleIdentifier; searchAndReplace(pbxPath, variables); searchAndReplace(projectPListPath, variables); searchAndReplace(config_file, variables); } - - // Remove all assets and JS modules installed by this plugin. - if (action == 'uninstall') { - var assets = plugin_et.findall('./asset'); - assets && assets.forEach(function(asset) { - var target = asset.attrib.target; - shell.rm('-rf', path.join(project_dir, 'www', target)); - }); - - shell.rm('-rf', path.join(project_dir, 'www', 'plugins', plugin_id)); - } - + if (callback) callback(); } function getRelativeDir(file) { @@ -280,126 +290,3 @@ function isBinaryPlist(filename) { // binary plists start with a magic header, "bplist" return buf.substring(0, 6) === 'bplist'; } - -function updatePlistFile(action, config_path, plugin_et) { - var hosts = plugin_et.findall('./access'), - platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this - plistEle = platformTag.find('./plugins-plist'), - external_hosts = []; - - // determine if this is a binary or ascii plist and choose the parser - // this is temporary until binary support is added to node-plist - var pl = (isBinaryPlist(config_path) ? bplist : plist); - - var plistObj = pl.parseFileSync(config_path); - - if (action == 'install') { - // add hosts to whitelist (ExternalHosts) in plist - hosts && hosts.forEach(function(host) { - plistObj.ExternalHosts.push(host.attrib['origin']); - }); - - // add plugin to plist - plistObj.Plugins[plistEle.attrib['key']] = plistEle.attrib['string']; - } else { - // remove hosts from whitelist (ExternalHosts) in plist - // check each entry in external hosts, only add it to the plist if - // it's not an entry added by this plugin - for(i=0; i < plistObj.ExternalHosts.length;i++) { - matched = false; - hosts && hosts.forEach(function(host) { - if(host === plistObj.ExternalHosts[i]) { - matched = true; - } - }); - if (!matched) { - external_hosts.push(plistObj.ExternalHosts[i]); - } - } - - // filtered the external hosts entries out, copy result - plistObj.ExternalHosts = external_hosts; - - delete plistObj.Plugins[plistEle.attrib['key']]; - } - - // write out plist - fs.writeFileSync(config_path, plist.build(plistObj)); -} - -function updateConfigXml(action, config_path, plugin_et) { - var hosts = plugin_et.findall('./access'), - platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this - plistEle = platformTag.find('./plugins-plist'), // use this for older that have plugins-plist - configChanges = getConfigChanges(platformTag), - base_config_path = path.basename(config_path); - - // edit configuration files - var xmlDoc = xml_helpers.parseElementtreeSync(config_path), - output, - pListOnly = plistEle; - - if (configChanges[path.basename(config_path)]) { - configChanges[path.basename(config_path)].forEach(function (val) { - if (val.find("plugin")) pListOnly = false; - }); - } - - if (pListOnly) { - // if the plugin supports the old plugins-plist element only - var name = plistEle.attrib.key; - var value = plistEle.attrib.string; - var pluginsEl = xmlDoc.find('plugins'); - if ( action == 'install') { - var new_plugin = new et.Element('plugin'); - new_plugin.attrib.name = name; - new_plugin.attrib.value = value; - pluginsEl.append(new_plugin); - } else { - var culprit = pluginsEl.find("plugin[@name='"+name+"']"); - pluginsEl.remove(0, culprit); - } - } - - // add whitelist hosts - root = et.Element("config-file"); - root.attrib['parent'] = '.' - hosts.forEach(function (tag) { - root.append(tag); - }); - - if (root.len()) { - (configChanges[path.basename(config_path)]) ? - configChanges[path.basename(config_path)].push(root) : - configChanges[path.basename(config_path)] = [root]; - } - - if (configChanges[path.basename(config_path)]) { - - configChanges[base_config_path].forEach(function (configNode) { - var selector = configNode.attrib["parent"], - children = configNode.findall('*'); - if( action == 'install') { - if (!xml_helpers.graftXML(xmlDoc, children, selector)) { - throw new Error('failed to add children to ' + selector + ' in ' + config_path); - } - } else { - if (!xml_helpers.pruneXML(xmlDoc, children, selector)) { - throw new Error('failed to remove children from ' + selector + ' in ' + config_path); - } - } - }); - } - - output = xmlDoc.write({indent: 4}); - fs.writeFileSync(config_path, output); -} - -// updates plist file and/or config.xml -function updateConfig(action, config_path, plugin_et) { - if(path.basename(config_path) == "config.xml") { - updateConfigXml(action, config_path, plugin_et); - } else { - updatePlistFile(action, config_path, plugin_et); - } -}
