Updated Branches: refs/heads/frameworks [created] 00ece4c51
[CB-4373] First pass at ios frameworks being handled in prepare stage. Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/00ece4c5 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/00ece4c5 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/00ece4c5 Branch: refs/heads/frameworks Commit: 00ece4c51d436e03bc250c123f1d3c511ebcdb77 Parents: 266c74f Author: Fil Maj <[email protected]> Authored: Mon Jul 29 19:11:00 2013 -0700 Committer: Fil Maj <[email protected]> Committed: Mon Jul 29 19:11:00 2013 -0700 ---------------------------------------------------------------------- spec/integration.spec.js | 85 ---------------- spec/platforms/ios.spec.js | 31 ------ src/install.js | 7 +- src/platforms/ios.js | 14 +-- src/util/config-changes.js | 209 +++++++++++++++++++++++++++------------- src/util/fs.js | 34 ------- 6 files changed, 144 insertions(+), 236 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/spec/integration.spec.js ---------------------------------------------------------------------- diff --git a/spec/integration.spec.js b/spec/integration.spec.js deleted file mode 100644 index 54b578e..0000000 --- a/spec/integration.spec.js +++ /dev/null @@ -1,85 +0,0 @@ -var plugman = require('../plugman'), - path = require('path'), - fs = require('fs'), - shell = require('shelljs'), - temp = path.join(__dirname, "..", ".tmp"), - plugins_dir = path.join(temp, 'plugins'); - -describe("integration", function () { - beforeEach(function () { - if ( !fs.existsSync(temp)) { - shell.mkdir("-p", temp); - } - }); - describe("local non-root depdencies", function () { - var project_src = path.join(__dirname, "projects", "android_one", "*"), - plugin_src = path.join(__dirname, "plugins", "dependencies", "B"), - project_dir = path.join(temp, "android"); - - beforeEach(function () { - shell.cp("-rf", project_src, project_dir); - }); - - it("should install dependencies from github", function () { - var flag = false, - installData; - - runs(function () { - plugman.install('android', project_dir, plugin_src, plugins_dir, {}, function (error) { - expect(error).not.toBeDefined(); - flag = true; - }); - }); - waitsFor(function () { return flag; }, "plugman install to finish", 10000); - runs(function () { - installData = require(path.join(plugins_dir, "android.json")); - expect(installData.installed_plugins).toEqual({ 'B': { PACKAGE_NAME: 'com.alunny.childapp'}}); - expect(installData.dependent_plugins).toEqual({ - 'D' : { PACKAGE_NAME: 'com.alunny.childapp'}, - 'E': { PACKAGE_NAME: 'com.alunny.childapp'} - }); - }); - //Cleanup - this.after(function () { - shell.rm("-rf", project_dir); - }); - }); - }); - - describe("blackberry10", function () { - var project_src = path.join(__dirname, "projects", "blackberry10", "*"), - plugin_src = path.join(__dirname, "plugins", "Contacts"), - project_dir = path.join(temp, "blackberry10"); - - beforeEach(function () { - shell.cp("-rf", project_src, project_dir); - }); - - it("should install dependencies from github", function () { - var flag = false, - installData; - - runs(function () { - plugman.install('blackberry10', project_dir, plugin_src, plugins_dir, {}, function (error) { - expect(error).not.toBeDefined(); - flag = true; - }); - }); - waitsFor(function () { return flag; }, "plugman install to finish", 10000); - runs(function () { - installData = require(path.join(plugins_dir, "blackberry10.json")); - expect(installData.installed_plugins).toEqual({ 'org.apache.cordova.core.contacts': { PACKAGE_NAME: 'cordovaExample'}}); - expect(installData.dependent_plugins).toEqual({ - 'com.blackberry.utils' : { PACKAGE_NAME: 'cordovaExample'}, - 'org.apache.cordova.blackberry10.pimlib': { PACKAGE_NAME: 'cordovaExample'} - }); - }); - //Cleanup - this.after(function () { - shell.rm("-rf", project_dir); - shell.rm("-rf", temp); - }); - }); - }); - -}); http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/spec/platforms/ios.spec.js ---------------------------------------------------------------------- diff --git a/spec/platforms/ios.spec.js b/spec/platforms/ios.spec.js index 3c34b8f..a19af13 100644 --- a/spec/platforms/ios.spec.js +++ b/spec/platforms/ios.spec.js @@ -240,24 +240,6 @@ describe('ios project handler', function() { expect(spy).toHaveBeenCalledWith('-R', path.join(dummyplugin, 'src', 'ios', 'DummyPlugin.bundle'), path.join(temp, 'SampleApp', 'Resources')); }); }); - - describe('of <framework> elements', function() { - beforeEach(function() { - shell.cp('-rf', ios_config_xml_project, temp); - }); - it('should call into xcodeproj\'s addFramework with weak false by default' ,function() { - var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak == undefined; }); - var spy = spyOn(proj_files.xcode, 'addFramework'); - ios.framework.install(frameworks[0], dummyplugin, temp, proj_files); - expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib').replace(/\\/g, '/'), {weak:false}); - }); - it('should pass in whether the framework is weak or not to xcodeproj.addFramework', function() { - var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak != undefined; });; - var spy = spyOn(proj_files.xcode, 'addFramework'); - ios.framework.install(frameworks[0], dummyplugin, temp, proj_files); - expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib').replace(/\\/g, '/'), {weak:true}); - }); - }); }); describe('uninstallation', function() { @@ -349,18 +331,5 @@ describe('ios project handler', function() { expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Resources', 'DummyPlugin.bundle')); }); }); - - describe('of <framework> elements', function() { - beforeEach(function() { - shell.cp('-rf', ios_config_xml_project, temp); - }); - it('should call into xcodeproj\'s removeFramework' ,function() { - var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak == undefined; }); - var spy = spyOn(proj_files.xcode, 'removeFramework'); - - ios.framework.uninstall(frameworks[0], temp, proj_files); - expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib').replace(/\\/g, '/')); - }); - }); }); }); http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/src/install.js ---------------------------------------------------------------------- diff --git a/src/install.js b/src/install.js index c3e125f..556ac7e 100644 --- a/src/install.js +++ b/src/install.js @@ -254,8 +254,7 @@ function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plu var sourceFiles = platformTag.findall('./source-file'), headerFiles = platformTag.findall('./header-file'), resourceFiles = platformTag.findall('./resource-file'), - libFiles = platformTag.findall('./lib-file'), - frameworks = platformTag.findall('./framework'); + libFiles = platformTag.findall('./lib-file'); assets = assets.concat(platformTag.findall('./asset')); // queue up native stuff @@ -271,10 +270,6 @@ function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plu actions.push(actions.createAction(handler["resource-file"].install, [resource, plugin_dir, project_dir], handler["resource-file"].uninstall, [resource, project_dir])); }); - frameworks && frameworks.forEach(function(framework) { - actions.push(actions.createAction(handler["framework"].install, [framework, plugin_dir, project_dir], handler["framework"].uninstall, [framework, project_dir])); - }); - libFiles && libFiles.forEach(function(lib) { actions.push(actions.createAction(handler["lib-file"].install, [lib, plugin_dir, project_dir], handler["lib-file"].uninstall, [lib, project_dir])); }); http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/src/platforms/ios.js ---------------------------------------------------------------------- diff --git a/src/platforms/ios.js b/src/platforms/ios.js index eea59b3..278746f 100644 --- a/src/platforms/ios.js +++ b/src/platforms/ios.js @@ -18,7 +18,7 @@ */ var path = require('path') - , fs = require('../util/fs') // use existsSync in 0.6.x + , fs = require('fs') , glob = require('glob') , xcode = require('xcode') , plist = require('plist') @@ -115,18 +115,6 @@ module.exports = { shell.rm('-rf', destFile); } }, - "framework":{ - install:function(framework_el, plugin_dir, project_dir, project) { - 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); - }, - uninstall:function(framework_el, project_dir, project) { - var src = framework_el.attrib['src']; - project.xcode.removeFramework(src); - } - }, parseIOSProjectFiles:function(project_dir) { // grab and parse pbxproj // we don't want CordovaLib's xcode project http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/src/util/config-changes.js ---------------------------------------------------------------------- diff --git a/src/util/config-changes.js b/src/util/config-changes.js index 4a4d7ac..36dfe4b 100644 --- a/src/util/config-changes.js +++ b/src/util/config-changes.js @@ -17,6 +17,19 @@ * */ +/* + * This module deals with shared configuration / dependency "stuff". That is: + * - XML configuration files such as config.xml or AndroidManifest.xml. + * - plist files in iOS + * - pbxproj files in iOS + * Essentially, any type of shared resources that we need to handle with awareness + * of how potentially multiple plugins depend on a single shared resource, should be + * handled in this module. + * + * The implementation uses an object as a hash table, with "leaves" of the table tracking + * reference counts. + */ + var fs = require('fs'), path = require('path'), glob = require('glob'), @@ -24,6 +37,7 @@ var fs = require('fs'), bplist = require('bplist-parser'), et = require('elementtree'), xml_helpers = require('./../util/xml-helpers'), + ios_parser = require('./../platforms/ios'), plist_helpers = require('./../util/plist-helpers'); function checkPlatform(platform) { @@ -102,6 +116,23 @@ module.exports = { munge['plugins-plist'][key] = value; } }); + // note down pbxproj framework munges in special section of munge obj + var frameworks = platformTag.findall('framework'); + frameworks.forEach(function(f) { + if (!munge['framework']) { + munge['framework'] = {}; + } + var file = f.attrib['src']; + var weak = f.attrib['weak']; + weak = (weak == undefined || weak == null || weak != 'true' ? 'false' : 'true'); + if (!munge['framework'][file]) { + munge['framework'][file] = {}; + } + if (!munge['framework'][file][weak]) { + munge['framework'][file][weak] = 0; + } + munge['framework'][file][weak] += 1; + }); } changes.forEach(function(change) { @@ -142,28 +173,40 @@ module.exports = { // global munge looks at all plugins' changes to config files var global_munge = platform_config.config_munge; + var plistObj, pbxproj; + if (platform == 'ios') { + if (global_munge['plugins-plist'] && config_munge['plugins-plist']) { + var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')); + if (plistfile.length > 0) { + plistfile = plistfile[0]; + // 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(plistfile) ? bplist : plist); + plistObj = pl.parseFileSync(plistfile); + } + } + if (global_munge['framework'] && config_munge['framework']) { + pbxproj = ios_parser.parseIOSProjectFiles(project_dir); + } + } + // Traverse config munge and decrement global munge Object.keys(config_munge).forEach(function(file) { if (file == 'plugins-plist' && platform == 'ios') { + // Handle plist files in ios if (global_munge[file]) { Object.keys(config_munge[file]).forEach(function(key) { - if (global_munge[file][key]) { - // prune from old plist if exists - var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')); - if (plistfile.length > 0) { - plistfile = plistfile[0]; - // 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(plistfile) ? bplist : plist); - var plistObj = pl.parseFileSync(plistfile); - delete plistObj.Plugins[key]; - fs.writeFileSync(plistfile, plist.build(plistObj)); - } + if (global_munge[file][key] && plistObj) { + delete plistObj.Plugins[key]; + // TODO: don't write out on every change, do it once. + fs.writeFileSync(plistfile, plist.build(plistObj)); delete global_munge[file][key]; } }); } } else if (global_munge[file]) { + // Handle arbitrary XML/pbxproj changes + var is_framework = (platform == 'ios' && file == 'framework'); Object.keys(config_munge[file]).forEach(function(selector) { if (global_munge[file][selector]) { Object.keys(config_munge[file][selector]).forEach(function(xml_child) { @@ -172,28 +215,36 @@ module.exports = { global_munge[file][selector][xml_child] -= 1; } if (global_munge[file][selector][xml_child] === 0) { - // this xml child is no longer necessary, prune it - // config.xml referenced in ios config changes refer to the project's config.xml, which we need to glob for. - var filepath = resolveConfigFilePath(project_dir, platform, file); - if (fs.existsSync(filepath)) { - if (path.extname(filepath) == '.xml') { - var xml_to_prune = [et.XML(xml_child)]; - var doc = xml_helpers.parseElementtreeSync(filepath); - if (xml_helpers.pruneXML(doc, xml_to_prune, selector)) { - // were good, write out the file! - fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8'); - } else { - // uh oh - throw new Error('pruning xml at selector "' + selector + '" from "' + filepath + '" during config uninstall went bad :('); - } - } else { - // plist file - var pl = (isBinaryPlist(filepath) ? bplist : plist); - var plistObj = pl.parseFileSync(filepath); - if (plist_helpers.prunePLIST(plistObj, xml_child, selector)) { - fs.writeFileSync(filepath, plist.build(plistObj)); + if (is_framework) { + // this is a .framework reference in ios files + pbxproj.xcode.removeFramework(selector); // in this case the 2nd-level key is the src attrib of <framework> els + // TODO: dont write on every loop eh + fs.writeFileSync(pbxproj.pbx, pbxproj.xcode.writeSync()); + } else { + // this xml child is no longer necessary, prune it + // config.xml referenced in ios config changes refer to the project's config.xml, which we need to glob for. + var filepath = resolveConfigFilePath(project_dir, platform, file); + if (fs.existsSync(filepath)) { + if (path.extname(filepath) == '.xml') { + var xml_to_prune = [et.XML(xml_child)]; + var doc = xml_helpers.parseElementtreeSync(filepath); + if (xml_helpers.pruneXML(doc, xml_to_prune, selector)) { + // were good, write out the file! + // TODO: don't write out on every change, do it once. + fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8'); + } else { + // uh oh + throw new Error('pruning xml at selector "' + selector + '" from "' + filepath + '" during config uninstall went bad :('); + } } else { - throw new Error('grafting to plist "' + filepath + '" during config install went bad :('); + // plist file + var pl = (isBinaryPlist(filepath) ? bplist : plist); + var plistObj = pl.parseFileSync(filepath); + if (plist_helpers.prunePLIST(plistObj, xml_child, selector)) { + fs.writeFileSync(filepath, plist.build(plistObj)); + } else { + throw new Error('grafting to plist "' + filepath + '" during config install went bad :('); + } } } } @@ -226,30 +277,43 @@ module.exports = { var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, project_dir, plugin_vars); // global munge looks at all plugins' changes to config files var global_munge = platform_config.config_munge; + + var pbxproj, plistObj; + if (platform == 'ios') { + if (config_munge['plugins-plist']) { + var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')); + if (plistfile.length > 0) { + plistfile = plistfile[0]; + // 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(plistfile) ? bplist : plist); + plistObj = pl.parseFileSync(plistfile); + } + } + if (config_munge['framework']) { + pbxproj = ios_parser.parseIOSProjectFiles(project_dir); + } + } // Traverse config munge and decrement global munge Object.keys(config_munge).forEach(function(file) { if (!global_munge[file]) { global_munge[file] = {}; } + var is_framework = (platform == 'ios' && file == 'framework'); Object.keys(config_munge[file]).forEach(function(selector) { + // Handle plist files on ios. if (file == 'plugins-plist' && platform == 'ios') { var key = selector; - if (!global_munge[file][key]) { + if (!global_munge[file][key] && plistObj) { // this key does not exist, so add it to plist global_munge[file][key] = config_munge[file][key]; - var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')); - if (plistfile.length > 0) { - plistfile = plistfile[0]; - // 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(plistfile) ? bplist : plist); - var plistObj = pl.parseFileSync(plistfile); - plistObj.Plugins[key] = config_munge[file][key]; - fs.writeFileSync(plistfile, plist.build(plistObj)); - } + plistObj.Plugins[key] = config_munge[file][key]; + // TODO: dont write on every loop eh + fs.writeFileSync(plistfile, plist.build(plistObj)); } } else { + // Handle arbitrary XML OR pbxproj framework stuff if (!global_munge[file][selector]) { global_munge[file][selector] = {}; } @@ -261,33 +325,44 @@ module.exports = { global_munge[file][selector][xml_child] += 1; } if (global_munge[file][selector][xml_child] == 1) { - // this xml child is new, graft it (only if config file exists) - // config file may be in a place not exactly specified in the target - var filepath = resolveConfigFilePath(project_dir, platform, file); - if (fs.existsSync(filepath)) { - // look at ext and do proper config change based on file type - if (path.extname(filepath) == '.xml') { - var xml_to_graft = [et.XML(xml_child)]; - var doc = xml_helpers.parseElementtreeSync(filepath); - if (xml_helpers.graftXML(doc, xml_to_graft, selector)) { - // were good, write out the file! - fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8'); + if (is_framework) { + var src = selector; // 2nd-level leaves are src path + // xml_child in this case is whether the framework should use weak or not + var opt = {weak: (xml_child != 'true' ? false : true)}; + pbxproj.xcode.addFramework(src, opt); + // TODO: dont write on every loop eh + fs.writeFileSync(pbxproj.pbx, pbxproj.xcode.writeSync()); + } else { + // this xml child is new, graft it (only if config file exists) + // config file may be in a place not exactly specified in the target + var filepath = resolveConfigFilePath(project_dir, platform, file); + if (fs.existsSync(filepath)) { + // look at ext and do proper config change based on file type + if (path.extname(filepath) == '.xml') { + var xml_to_graft = [et.XML(xml_child)]; + // TODO: could parse the filepath once per unique target instead of on every change + var doc = xml_helpers.parseElementtreeSync(filepath); + if (xml_helpers.graftXML(doc, xml_to_graft, selector)) { + // were good, write out the file! + fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8'); + } else { + // uh oh + throw new Error('grafting xml at selector "' + selector + '" from "' + filepath + '" during config install went bad :('); + } } else { - // uh oh - throw new Error('grafting xml at selector "' + selector + '" from "' + filepath + '" during config install went bad :('); + // plist file + var pl = (isBinaryPlist(filepath) ? bplist : plist); + // TODO: could parse the filepath once per unique target instead of on every change + var plistObj = pl.parseFileSync(filepath); + if (plist_helpers.graftPLIST(plistObj, xml_child, selector)) { + fs.writeFileSync(filepath, plist.build(plistObj)); + } else { + throw new Error('grafting to plist "' + filepath + '" during config install went bad :('); + } } } else { - // plist file - var pl = (isBinaryPlist(filepath) ? bplist : plist); - var plistObj = pl.parseFileSync(filepath); - if (plist_helpers.graftPLIST(plistObj, xml_child, selector)) { - fs.writeFileSync(filepath, plist.build(plistObj)); - } else { - throw new Error('grafting to plist "' + filepath + '" during config install went bad :('); - } + // TODO: ignore if file doesnt exist? } - } else { - // ignore if file doesnt exist } } }); http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/00ece4c5/src/util/fs.js ---------------------------------------------------------------------- diff --git a/src/util/fs.js b/src/util/fs.js deleted file mode 100644 index 3d9f374..0000000 --- a/src/util/fs.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2013 Anis Kadri - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - -var fs = require('fs'); - -// polyfill for Node 0.6.x -if (!fs.existsSync) { - fs.existsSync = function (path) { - try { - fs.statSync(path); - return true; - } catch (e) { - return false; - } - } -} - -module.exports = fs;
