http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/platforms/PlatformApiPoly.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/platforms/PlatformApiPoly.js b/cordova-lib/src/platforms/PlatformApiPoly.js new file mode 100644 index 0000000..183bc4d --- /dev/null +++ b/cordova-lib/src/platforms/PlatformApiPoly.js @@ -0,0 +1,706 @@ +/** + 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 Q = require('q'); +var fs = require('fs'); +var path = require('path'); +var unorm = require('unorm'); +var shell = require('shelljs'); +var semver = require('semver'); + +var superspawn = require('../cordova/superspawn'); +var xmlHelpers = require('../util/xml-helpers'); +var common = require('../plugman/platforms/common'); +var knownPlatforms = require('./platforms'); +var CordovaError = require('../CordovaError'); +var PluginInfo = require('../PluginInfo'); +var ConfigParser = require('../configparser/ConfigParser'); +var PlatformJson = require('../plugman/util/PlatformJson'); +var ActionStack = require('../plugman/util/action-stack'); +var PlatformMunger = require('../plugman/util/config-changes').PlatformMunger; +var PluginInfoProvider = require('../PluginInfoProvider'); + +/** + * Class, that acts as abstraction over particular platform. Encapsulates the + * platform's properties and methods. + * + * Platform that implements own PlatformApi instance _should implement all + * prototype methods_ of this class to be fully compatible with cordova-lib. + * + * The PlatformApi instance also should define the following field: + * + * * platform: String that defines a platform name. + */ +function PlatformApiPoly(platform, platformRootDir) { + if (!platform) throw new CordovaError('\'platform\' argument is missing'); + if (!platformRootDir) throw new CordovaError('platformRootDir argument is missing'); + + this.root = platformRootDir; + this.platform = platform; + + if (!(platform in knownPlatforms)) + throw new CordovaError('Unknown platform ' + platform); + + var ParserConstructor = require(knownPlatforms[platform].parser_file); + this._parser = new ParserConstructor(this.root); + this._handler = require(knownPlatforms[platform].handler_file); + + this._platformJson = PlatformJson.load(this.root, platform); + this._pluginInfoProvider = new PluginInfoProvider(); + this._munger = new PlatformMunger(platform, this.root, this._platformJson, this._pluginInfoProvider); + + this._config = new ConfigParser(this.getPlatformInfo().locations.configXml); +} + +/** + * Installs platform to specified directory and creates a platform project. + * + * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a + * project structure and configuration, that should be applied to new platform + * (contains platform's target location and ConfigParser instance for + * project's config). This argument is optional and if not defined, this means + * that platform is used as standalone project and is not a part of cordova + * project. + * @param {Object} options An options object. The most common options are: + * @param {String} options.customTemplate A path to custom template, that + * should override the default one from platform. + * @param {Boolean} options.link Flag that indicates that platform's sources + * will be linked to installed platform instead of copying. + * + * @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ +PlatformApiPoly.createPlatform = function (cordovaProject, options) { + if (!options || !options.platformDetails) + return Q.reject(CordovaError('Failed to find platform\'s \'create\' script. ' + + 'Either \'options\' parameter or \'platformDetails\' option is missing')); + + var command = path.join(options.platformDetails.libDir, 'bin', 'create'); + var commandArguments = getCreateArgs(cordovaProject, options); + + return superspawn.spawn(command, commandArguments, + { printCommand: true, stdio: 'inherit', chmod: true }) + .then(function () { + var destination = path.join(cordovaProject.locations.platforms, options.platformDetails.platform); + var platformApi = knownPlatforms + .getPlatformApi(options.platformDetails.platform, destination); + copyCordovaSrc(options.platformDetails.libDir, platformApi.getPlatformInfo()); + return platformApi; + }); +}; + +/** + * Updates already installed platform. + * + * @param {CordovaProject} cordovaProject A CordovaProject instance, that + * defines a project structure and configuration, that should be applied to + * new platform (contains platform's target location and ConfigParser instance + * for project's config). This argument is optional and if not defined, this + * means that platform is used as standalone project and is not a part of + * cordova project. + * @param {Object} options An options object. The most common options are: + * @param {String} options.customTemplate A path to custom template, that + * should override the default one from platform. + * @param {Boolean} options.link Flag that indicates that platform's sources + * will be linked to installed platform instead of copying. + * + * @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ +PlatformApiPoly.updatePlatform = function (cordovaProject, options) { + if (!options || !options.platformDetails) + return Q.reject(CordovaError('Failed to find platform\'s \'create\' script. ' + + 'Either \'options\' parameter or \'platformDetails\' option is missing')); + + var command = path.join(options.platformDetails.libDir, 'bin', 'update'); + var destination = path.join(cordovaProject.locations.platforms, options.platformDetails.platform); + + return superspawn.spawn(command, [destination], + { printCommand: true, stdio: 'inherit', chmod: true }) + .then(function () { + var platformApi = knownPlatforms + .getPlatformApi(options.platformDetails.platform, destination); + copyCordovaSrc(options.platformDetails.libDir, platformApi.getPlatformInfo()); + return platformApi; + }); +}; + +/** + * Gets a CordovaPlatform object, that represents the platform structure. + * + * @return {CordovaPlatform} A structure that contains the description of + * platform's file structure and other properties of platform. + */ +PlatformApiPoly.prototype.getPlatformInfo = function () { + var self = this; + var result = {}; + result.locations = { + www: self._parser.www_dir(), + platformWww: path.join(self.root, 'platform_www'), + configXml: self._parser.config_xml(), + // NOTE: Due to platformApi spec we need to return relative paths here + cordovaJs: path.relative(self.root, self._parser.cordovajs_path.call(self.parser, self.root)), + cordovaJsSrc: path.relative(self.root, self._parser.cordovajs_src_path.call(self.parser, self.root)) + }; + result.root = self.root; + result.name = self.platform; + result.version = knownPlatforms[self.platform].version; + result.projectConfig = self._config; + + return result; +}; + +/** + * Updates installed platform with provided www assets and new app + * configuration. This method is required for CLI workflow and will be called + * each time before build, so the changes, made to app configuration and www + * code, will be applied to platform. + * + * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a + * project structure and configuration, that should be applied to platform + * (contains project's www location and ConfigParser instance for project's + * config). + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +PlatformApiPoly.prototype.prepare = function (cordovaProject) { + + // First cleanup current config and merge project's one into own + var defaultConfig = path.join(this.root, 'cordova', 'defaults.xml'); + var ownConfig = this._config.path; + var sourceCfg = cordovaProject.projectConfig.path; + // If defaults.xml is present, overwrite platform config.xml with it. + // Otherwise save whatever is there as defaults so it can be + // restored or copy project config into platform if none exists. + if (fs.existsSync(defaultConfig)) { + // events.emit('verbose', 'Generating config.xml from defaults for platform "' + this.platform + '"'); + shell.cp('-f', defaultConfig, ownConfig); + this._config = new ConfigParser(ownConfig); + } else if (fs.existsSync(ownConfig)) { + shell.cp('-f', ownConfig, defaultConfig); + } else { + shell.cp('-f', sourceCfg.path, ownConfig); + this._config = new ConfigParser(ownConfig); + } + + xmlHelpers.mergeXml(cordovaProject.projectConfig.doc.getroot(), + this._config.doc.getroot(), this.platform, true); + // CB-6976 Windows Universal Apps. For smooth transition and to prevent mass api failures + // we allow using windows8 tag for new windows platform + if (this.platform == 'windows') { + xmlHelpers.mergeXml(cordovaProject.projectConfig.doc.getroot(), + this._config.doc.getroot(), 'windows8', true); + } + this._config.write(); + + // Update own www dir with project's www assets and plugins' assets and js-files + this._parser.update_www(cordovaProject.locations.www); + + this._munger.reapply_global_munge().save_all(); + + // update project according to config.xml changes. + return this._parser.update_project(cordovaProject.projectConfig); +}; + +/** + * Installs a new plugin into platform. This method only copies non-www files + * (sources, libs, etc.) to platform. It also doesn't resolves the + * dependencies of plugin. Both of handling of www files, such as assets and + * js-files and resolving dependencies are the responsibility of caller. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * @param {Object} installOptions An options object. Possible options below: + * @param {Boolean} installOptions.link: Flag that specifies that plugin + * sources will be symlinked to app's directory instead of copying (if + * possible). + * @param {Object} installOptions.variables An object that represents + * variables that will be used to install plugin. See more details on plugin + * variables in documentation: + * https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +PlatformApiPoly.prototype.addPlugin = function (plugin, installOptions) { + + if (!plugin || !(plugin instanceof PluginInfo)) + return Q.reject('The parameter is incorrect. The first parameter ' + + 'should be valid PluginInfo instance'); + + installOptions = installOptions || {}; + installOptions.variables = installOptions.variables || {}; + + var self = this; + var actions = new ActionStack(); + var projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); + + // gather all files needs to be handled during install + plugin.getFilesAndFrameworks(this.platform) + .concat(plugin.getAssets(this.platform)) + .concat(plugin.getJsModules(this.platform)) + .forEach(function(item) { + actions.push(actions.createAction( + self._getInstaller(item.itemType), [item, plugin.dir, plugin.id, installOptions, projectFile], + self._getUninstaller(item.itemType), [item, plugin.dir, plugin.id, installOptions, projectFile])); + }); + + // run through the action stack + return actions.process(this.platform, this.root) + .then(function () { + if (projectFile) { + projectFile.write(); + } + + // Add PACKAGE_NAME variable into vars + if (!installOptions.variables.PACKAGE_NAME) { + installOptions.variables.PACKAGE_NAME = self._handler.package_name(self.root); + } + + self._munger + // Ignore passed `is_top_level` option since platform itself doesn't know + // anything about managing dependencies - it's responsibility of caller. + .add_plugin_changes(plugin, installOptions.variables, /*is_top_level=*/true, /*should_increment=*/true) + .save_all(); + + var targetDir = installOptions.usePlatformWww ? + self.getPlatformInfo().locations.platformWww : + self.getPlatformInfo().locations.www; + + self._addModulesInfo(plugin, targetDir); + }); +}; + +/** + * Removes an installed plugin from platform. + * + * Since method accepts PluginInfo instance as input parameter instead of plugin + * id, caller shoud take care of managing/storing PluginInfo instances for + * future uninstalls. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +PlatformApiPoly.prototype.removePlugin = function (plugin, uninstallOptions) { + + var self = this; + var actions = new ActionStack(); + var projectFile = this._handler.parseProjectFile && this._handler.parseProjectFile(this.root); + + // queue up plugin files + plugin.getFilesAndFrameworks(this.platform) + .concat(plugin.getAssets(this.platform)) + .concat(plugin.getJsModules(this.platform)) + .filter(function (item) { + // CB-5238 Skip (don't uninstall) non custom frameworks. + return !(item.itemType == 'framework' && !item.custom); + }).forEach(function(item) { + actions.push(actions.createAction( + self._getUninstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile], + self._getInstaller(item.itemType), [item, plugin.dir, plugin.id, uninstallOptions, projectFile])); + }); + + // run through the action stack + return actions.process(this.platform, this.root) + .then(function() { + if (projectFile) { + projectFile.write(); + } + + self._munger + // Ignore passed `is_top_level` option since platform itself doesn't know + // anything about managing dependencies - it's responsibility of caller. + .remove_plugin_changes(plugin, /*is_top_level=*/true) + .save_all(); + + var targetDir = uninstallOptions.usePlatformWww ? + self.getPlatformInfo().locations.platformWww : + self.getPlatformInfo().locations.www; + + self._removeModulesInfo(plugin, targetDir); + // Remove stale plugin directory + // TODO: this should be done by plugin files uninstaller + shell.rm('-rf', path.resolve(self.root, 'Plugins', plugin.id)); + }); +}; + +PlatformApiPoly.prototype.updatePlugin = function (plugin, updateOptions) { + var self = this; + + // Set up assets installer to copy asset files into platform_www dir instead of www + updateOptions = updateOptions || {}; + updateOptions.usePlatformWww = true; + + return this.removePlugin(plugin, updateOptions) + .then(function () { + return self.addPlugin(plugin, updateOptions); + }); +}; + +/** + * Builds an application package for current platform. + * + * @param {Object} buildOptions A build options. This object's structure is + * highly depends on platform's specific. The most common options are: + * @param {Boolean} buildOptions.debug Indicates that packages should be + * built with debug configuration. This is set to true by default unless the + * 'release' option is not specified. + * @param {Boolean} buildOptions.release Indicates that packages should be + * built with release configuration. If not set to true, debug configuration + * will be used. + * @param {Boolean} buildOptions.device Specifies that built app is intended + * to run on device + * @param {Boolean} buildOptions.emulator: Specifies that built app is + * intended to run on emulator + * @param {String} buildOptions.target Specifies the device id that will be + * used to run built application. + * @param {Boolean} buildOptions.nobuild Indicates that this should be a + * dry-run call, so no build artifacts will be produced. + * @param {String[]} buildOptions.archs Specifies chip architectures which + * app packages should be built for. List of valid architectures is depends on + * platform. + * @param {String} buildOptions.buildConfig The path to build configuration + * file. The format of this file is depends on platform. + * @param {String[]} buildOptions.argv Raw array of command-line arguments, + * passed to `build` command. The purpose of this property is to pass a + * platform-specific arguments, and eventually let platform define own + * arguments processing logic. + * + * @return {Promise<Object[]>} A promise either fulfilled with an array of build + * artifacts (application packages) if package was built successfully, + * or rejected with CordovaError. The resultant build artifact objects is not + * strictly typed and may conatin arbitrary set of fields as in sample below. + * + * { + * architecture: 'x86', + * buildType: 'debug', + * path: '/path/to/build', + * type: 'app' + * } + * + * The return value in most cases will contain only one item but in some cases + * there could be multiple items in output array, e.g. when multiple + * arhcitectures is specified. + */ +PlatformApiPoly.prototype.build = function(buildOptions) { + var command = path.join(this.root, 'cordova', 'build'); + var commandArguments = getBuildArgs(buildOptions); + return superspawn.spawn(command, commandArguments, { + printCommand: true, stdio: 'inherit', chmod: true }); +}; + +/** + * Builds an application package for current platform and runs it on + * specified/default device. If no 'device'/'emulator'/'target' options are + * specified, then tries to run app on default device if connected, otherwise + * runs the app on emulator. + * + * @param {Object} runOptions An options object. The structure is the same + * as for build options. + * + * @return {Promise} A promise either fulfilled if package was built and ran + * successfully, or rejected with CordovaError. + */ +PlatformApiPoly.prototype.run = function(runOptions) { + var command = path.join(this.root, 'cordova', 'run'); + var commandArguments = getBuildArgs(runOptions); + return superspawn.spawn(command, commandArguments, { + printCommand: true, stdio: 'inherit', chmod: true }); +}; + +/** + * Cleans out the build artifacts from platform's directory. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError. + */ +PlatformApiPoly.prototype.clean = function() { + var cmd = path.join(this.root, 'cordova', 'clean'); + return superspawn.spawn(cmd, [], { printCommand: true, stdio: 'inherit', chmod: true }); +}; + +/** + * Performs a requirements check for current platform. Each platform defines its + * own set of requirements, which should be resolved before platform can be + * built successfully. + * + * @return {Promise<Requirement[]>} Promise, resolved with set of Requirement + * objects for current platform. + */ +PlatformApiPoly.prototype.requirements = function() { + var modulePath = path.join(this.root, 'cordova', 'lib', 'check_reqs'); + try { + return require(modulePath).check_all(); + } catch (e) { + var errorMsg = 'Failed to check requirements for ' + this.platform + ' platform. ' + + 'check_reqs module is missing for platfrom. Skipping it...'; + return Q.reject(errorMsg); + } +}; + +module.exports = PlatformApiPoly; + +/** + * Converts arguments, passed to createPlatform to command-line args to + * 'bin/create' script for specific platform. + * + * @param {ProjectInfo} project A current project information. The vauest + * which this method interested in are project.config - config.xml abstraction + * - and platformsLocation - to get install destination. + * @param {Object} options Set of properties for create script. + * + * @return {String[]} An array or arguments which can be passed to + * 'bin/create'. + */ +function getCreateArgs(project, options) { + var platformName = options.platformDetails.platform; + var platformVersion = options.platformDetails.version; + + var args = []; + args.push(path.join(project.locations.platforms, platformName)); // output + args.push(project.projectConfig.packageName().replace(/[^\w.]/g,'_')); + // CB-6992 it is necessary to normalize characters + // because node and shell scripts handles unicode symbols differently + // We need to normalize the name to NFD form since iOS uses NFD unicode form + args.push(platformName == 'ios' ? unorm.nfd(project.projectConfig.name()) : project.projectConfig.name()); + + if (options.customTemplate) { + args.push(options.customTemplate); + } + + if (/android|ios/.exec(platformName) && + semver.gt(platformVersion, '3.3.0')) args.push('--cli'); + + if (options.link) args.push('--link'); + + if (platformName === 'android' && semver.gte(platformVersion, '4.0.0-dev')) { + var activityName = project.projectConfig.android_activityName(); + if (activityName) { + args.push('--activity-name', activityName.replace(/\W/g, '')); + } + } + + return args; +} + +/** + * Reconstructs the buildOptions tat will be passed along to platform scripts. + * This is an ugly temporary fix. The code spawning or otherwise calling into + * platform code should be dealing with this based on the parsed args object. + * + * @param {Object} options A build options set, passed to `build` method + * + * @return {String[]} An array or arguments which can be passed to + * `create` build script. + */ +function getBuildArgs(options) { + // if no options passed, empty object will be returned + if (!options) return []; + + var downstreamArgs = []; + var argNames =[ + 'debug', + 'release', + 'device', + 'emulator', + 'nobuild', + 'list' + ]; + + argNames.forEach(function(flag) { + if (options[flag]) { + downstreamArgs.push('--' + flag); + } + }); + + if (options.buildConfig) { + downstreamArgs.push('--buildConfig=' + options.buildConfig); + } + if (options.target) { + downstreamArgs.push('--target=' + options.target); + } + if (options.archs) { + downstreamArgs.push('--archs=' + options.archs); + } + + var unparsedArgs = options.argv || []; + return downstreamArgs.concat(unparsedArgs); +} + +/** + * Removes the specified modules from list of installed modules and updates + * platform_json and cordova_plugins.js on disk. + * + * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules + * needs to be added. + * @param {String} targetDir The directory, where updated cordova_plugins.js + * should be written to. + */ +PlatformApiPoly.prototype._addModulesInfo = function(plugin, targetDir) { + var installedModules = this._platformJson.root.modules || []; + + var installedPaths = installedModules.map(function (installedModule) { + return installedModule.file; + }); + + var modulesToInstall = plugin.getJsModules(this.platform) + .filter(function (moduleToInstall) { + return installedPaths.indexOf(moduleToInstall.file) === -1; + }).map(function (moduleToInstall) { + var moduleName = plugin.id + '.' + ( moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1] ); + var obj = { + file: ['plugins', plugin.id, moduleToInstall.src].join('/'), + id: moduleName + }; + if (moduleToInstall.clobbers.length > 0) { + obj.clobbers = moduleToInstall.clobbers.map(function(o) { return o.target; }); + } + if (moduleToInstall.merges.length > 0) { + obj.merges = moduleToInstall.merges.map(function(o) { return o.target; }); + } + if (moduleToInstall.runs) { + obj.runs = true; + } + + return obj; + }); + + this._platformJson.root.modules = installedModules.concat(modulesToInstall); + this._writePluginModules(targetDir); + this._platformJson.save(); +}; + +/** + * Removes the specified modules from list of installed modules and updates + * platform_json and cordova_plugins.js on disk. + * + * @param {PluginInfo} plugin PluginInfo instance for plugin, which modules + * needs to be removed. + * @param {String} targetDir The directory, where updated cordova_plugins.js + * should be written to. + */ +PlatformApiPoly.prototype._removeModulesInfo = function(plugin, targetDir) { + var installedModules = this._platformJson.root.modules || []; + var modulesToRemove = plugin.getJsModules(this.platform) + .map(function (jsModule) { + return ['plugins', plugin.id, jsModule.src].join('/'); + }); + + var updatedModules = installedModules + .filter(function (installedModule) { + return (modulesToRemove.indexOf(installedModule.file) === -1); + }); + + this._platformJson.root.modules = updatedModules; + this._writePluginModules(targetDir); + this._platformJson.save(); +}; + +/** + * Fetches all installed modules, generates cordova_plugins contents and writes + * it to file. + * + * @param {String} targetDir Directory, where write cordova_plugins.js to. + * Ususally it is either <platform>/www or <platform>/platform_www + * directories. + */ +PlatformApiPoly.prototype._writePluginModules = function (targetDir) { + var self = this; + // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js + var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n'; + final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, ' ') + ';\n'; + final_contents += 'module.exports.metadata = \n'; + final_contents += '// TOP OF METADATA\n'; + + var pluginMetadata = Object.keys(this._platformJson.root.installed_plugins) + .reduce(function (metadata, plugin) { + metadata[plugin] = self._platformJson.root.installed_plugins[plugin].version; + return metadata; + }, {}); + + final_contents += JSON.stringify(pluginMetadata, null, ' ') + '\n'; + final_contents += '// BOTTOM OF METADATA\n'; + final_contents += '});'; // Close cordova.define. + + shell.mkdir('-p', targetDir); + fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8'); +}; + +PlatformApiPoly.prototype._getInstaller = function(type) { + var self = this; + return function (item, plugin_dir, plugin_id, options, project) { + var installer = self._handler[type] || common[type]; + + var wwwDest = options.usePlatformWww ? + self.getPlatformInfo().locations.platformWww : + self._handler.www_dir(self.root); + + var installerArgs = type === 'asset' ? [wwwDest] : + type === 'js-module' ? [plugin_id, wwwDest]: + [self.root, plugin_id, options, project]; + + installer.install.apply(null, [item, plugin_dir].concat(installerArgs)); + }; +}; + +PlatformApiPoly.prototype._getUninstaller = function(type) { + var self = this; + return function (item, plugin_dir, plugin_id, options, project) { + var uninstaller = self._handler[type] || common[type]; + + var wwwDest = options.usePlatformWww ? + self.getPlatformInfo().locations.platformWww : + self._handler.www_dir(self.root); + + var uninstallerArgs = (type === 'asset' || type === 'js-module') ? [wwwDest, plugin_id] : + [self.root, plugin_id, options, project]; + + uninstaller.uninstall.apply(null, [item].concat(uninstallerArgs)); + }; +}; + +/** + * Copies cordova.js itself and cordova-js source into installed/updated + * platform's `platform_www` directory. + * + * @param {String} sourceLib Path to platform library. Required to acquire + * cordova-js sources. + * @param {PlatformInfo} platformInfo PlatformInfo structure, required for + * detecting copied files destination. + */ +function copyCordovaSrc(sourceLib, platformInfo) { + // Copy the cordova.js file to platforms/<platform>/platform_www/ + // The www dir is nuked on each prepare so we keep cordova.js in platform_www + shell.mkdir('-p', platformInfo.locations.platformWww); + shell.cp('-f', path.join(platformInfo.locations.www, 'cordova.js'), + path.join(platformInfo.locations.platformWww, 'cordova.js')); + + // Copy cordova-js-src directory into platform_www directory. + // We need these files to build cordova.js if using browserify method. + var cordovaJsSrcPath = path.resolve(sourceLib, platformInfo.locations.cordovaJsSrc); + + //only exists for platforms that have shipped cordova-js-src directory + if(fs.existsSync(cordovaJsSrcPath)) { + shell.cp('-rf', cordovaJsSrcPath, platformInfo.locations.platformWww); + } +}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/platforms/platforms.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/platforms/platforms.js b/cordova-lib/src/platforms/platforms.js index cc0e492..d71570f 100644 --- a/cordova-lib/src/platforms/platforms.js +++ b/cordova-lib/src/platforms/platforms.js @@ -17,89 +17,47 @@ under the License. */ +var path = require('path'); +var util = require('../cordova/util'); var platforms = require('./platformsConfig.json'); -// Remove this block soon. The parser property is no longer used in -// cordova-lib but some downstream tools still use it. -var addModuleProperty = require('../cordova/util').addModuleProperty; -Object.keys(platforms).forEach(function(key) { - var obj = platforms[key]; - if (obj.parser_file) { - addModuleProperty(module, 'parser', obj.parser_file, false, obj); - } -}); - - // Avoid loading the same platform projects more than once (identified by path) -var cachedProjects = {}; - -var PARSER_PUBLIC_METHODS = [ - 'config_xml', - 'cordovajs_path', - 'cordovajs_src_path', - 'update_from_config', - 'update_project', - 'update_www', - 'www_dir', -]; - -var HANDLER_PUBLIC_METHODS = [ - 'package_name', - 'parseProjectFile', - 'purgeProjectFileCache', -]; - +var cachedApis = {}; -// A single class that exposes functionality from platform specific files from -// both places cordova/metadata and plugman/platforms. Hopefully, to be soon -// replaced by real unified platform specific classes. -function PlatformProjectAdapter(platform, platformRootDir) { - var self = this; - self.root = platformRootDir; - self.platform = platform; - var ParserConstructor = require(platforms[platform].parser_file); - self.parser = new ParserConstructor(platformRootDir); - self.handler = require(platforms[platform].handler_file); +// getPlatformApi() should be the only method of instantiating the +// PlatformProject classes for now. +function getPlatformApi(platform, platformRootDir) { - // Expose all public methods from the parser and handler, properly bound. - PARSER_PUBLIC_METHODS.forEach(function(method) { - self[method] = self.parser[method].bind(self.parser); - }); + // if platformRootDir is not specified, try to detect it first + if (!platformRootDir) { + var projectRootDir = util.isCordova(); + platformRootDir = projectRootDir && path.join(projectRootDir, 'platforms', platform); + } - HANDLER_PUBLIC_METHODS.forEach(function(method) { - if (self.handler[method]) { - self[method] = self.handler[method].bind(self.handler); - } - }); + if (!platformRootDir) { + // If platformRootDir is still undefined, then we're probably is not inside of cordova project + throw new Error('Current location is not a Cordova project'); + } - self.getInstaller = function(type) { - function installWrapper(item, plugin_dir, plugin_id, options, project) { - self.handler[type].install(item, plugin_dir, self.root, plugin_id, options, project); - } - return installWrapper; - }; + var cached = cachedApis[platformRootDir]; + if (cached && cached.platform == platform) return cached; - self.getUninstaller = function(type) { - function uninstallWrapper(item, plugin_id, options, project) { - self.handler[type].uninstall(item, self.root, plugin_id, options, project); - } - return uninstallWrapper; - }; -} + if (!platforms[platform]) throw new Error('Unknown platform ' + platform); -// getPlatformProject() should be the only method of instantiating the -// PlatformProject classes for now. -function getPlatformProject(platform, platformRootDir) { - var cached = cachedProjects[platformRootDir]; - if (cached && cached.platform == platform) { - return cachedProjects[platformRootDir]; - } else if (platforms[platform]) { - var adapter = new PlatformProjectAdapter(platform, platformRootDir); - cachedProjects[platformRootDir] = adapter; - return adapter; - } else { - throw new Error('Unknown platform ' + platform); + var PlatformApi; + try { + // First we need to find whether platform exposes its' API via js module + // If it has, then we have to require it and extend BasePlatformApi + // with platform's API. + var platformApiModule = path.join(platformRootDir, 'cordova', 'Api.js'); + PlatformApi = require(platformApiModule); + } catch (err) { + PlatformApi = require('./PlatformApiPoly'); } + + var platformApi = new PlatformApi(platform, platformRootDir); + cachedApis[platformRootDir] = platformApi; + return platformApi; } module.exports = platforms; @@ -107,6 +65,5 @@ module.exports = platforms; // We don't want these methods to be enumerable on the platforms object, because we expect enumerable properties of the // platforms object to be platforms. Object.defineProperties(module.exports, { - 'getPlatformProject': {value: getPlatformProject, configurable: true, writable: true}, - 'PlatformProjectAdapter': {value: PlatformProjectAdapter, configurable: true, writable: true} + 'getPlatformApi': {value: getPlatformApi, configurable: true, writable: true} }); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/browserify.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/browserify.js b/cordova-lib/src/plugman/browserify.js new file mode 100644 index 0000000..8d5581b --- /dev/null +++ b/cordova-lib/src/plugman/browserify.js @@ -0,0 +1,181 @@ +/** + 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. +*/ + +/* jshint unused:false, expr:true */ + +var platform_modules = require('../platforms/platforms'), + path = require('path'), + aliasify = require('aliasify'), + config_changes = require('./util/config-changes'), + common = require('./platforms/common'), + fs = require('fs'), + childProcess = require('child_process'), + util = require('util'), + events = require('../events'), + plugman = require('./plugman'), + bundle = require('cordova-js/tasks/lib/bundle-browserify'), + writeLicenseHeader = require('cordova-js/tasks/lib/write-license-header'), + Q = require('q'), + computeCommitId = require('cordova-js/tasks/lib/compute-commit-id'), + Readable = require('stream').Readable; + +var PlatformJson = require('./util/PlatformJson'); +var PluginInfoProvider = require('../PluginInfoProvider'); + +function generateFinalBundle(platform, libraryRelease, outReleaseFile, commitId, platformVersion) { + var deferred = Q.defer(); + var outReleaseFileStream = fs.createWriteStream(outReleaseFile); + var time = new Date().valueOf(); + var symbolList = null; + + writeLicenseHeader(outReleaseFileStream, platform, commitId, platformVersion); + + var releaseBundle = libraryRelease.bundle(); + + releaseBundle.pipe(outReleaseFileStream); + + outReleaseFileStream.on('finish', function() { + var newtime = new Date().valueOf() - time; + plugman.emit('verbose', 'generated cordova.' + platform + '.js @ ' + commitId + ' in ' + newtime + 'ms'); + deferred.resolve(); + // TODO clean up all the *.browserify files + }); + + outReleaseFileStream.on('error', function(err) { + events.emit('log', 'error while generating cordova.js'); + deferred.reject(); + }); + return deferred.promise; +} + +function computeCommitIdSync() { + var deferred = Q.defer(); + computeCommitId(function(cId){ + deferred.resolve(cId); + }); + return deferred.promise; +} + +function getPlatformVersion(cId, project_dir) { + var deferred = Q.defer(); + //run version script for each platform to get platformVersion + var versionPath = path.join(project_dir, '/cordova/version'); + childProcess.exec('"' + versionPath + '"', function(err, stdout, stderr) { + if (err) { + events.emit('log', 'Error running platform version script'); + events.emit('log', err); + deferred.resolve('N/A'); + } else { + deferred.resolve(stdout.trim()); + } + }); + return deferred.promise; +} + +module.exports = function doBrowserify (project, platformApi, pluginInfoProvider) { + // Process: + // - Do config munging by calling into config-changes module + // - 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/cordova_plugins.json. + // This file is not really used. Maybe cordova app harness + var platform = platformApi.platform; + events.emit('verbose', 'Preparing ' + platform + ' browserify project'); + pluginInfoProvider = pluginInfoProvider || new PluginInfoProvider(); // Allow null for backwards-compat. + var platformJson = PlatformJson.load(project.locations.plugins, platform); + var wwwDir = platformApi.getPlatformInfo().locations.www; + + var commitId; + return computeCommitIdSync() + .then(function(cId){ + commitId = cId; + return getPlatformVersion(commitId, platformApi.root); + }).then(function(platformVersion){ + var libraryRelease = bundle(platform, false, commitId, platformVersion); + + var pluginMetadata = {}; + var modulesMetadata = []; + + var plugins = Object.keys(platformJson.root.installed_plugins).concat(Object.keys(platformJson.root.dependent_plugins)); + events.emit('verbose', 'Iterating over installed plugins:', plugins); + plugins.forEach(function (plugin) { + var pluginDir = path.join(project.locations.plugins, plugin); + var pluginInfo = pluginInfoProvider.get(pluginDir); + // pluginMetadata is a mapping from plugin IDs to versions. + pluginMetadata[pluginInfo.id] = pluginInfo.version; + + // Copy www assets described in <asset> tags. + pluginInfo.getAssets(platform) + .forEach(function(asset) { + common.asset.install(asset, pluginDir, wwwDir); + }); + + pluginInfo.getJsModules(platform) + .forEach(function(jsModule) { + var moduleName = jsModule.name ? jsModule.name : path.basename(jsModule.src, '.js'); + var moduleId = pluginInfo.id + '.' + moduleName; + var moduleMetadata = {file: jsModule.src, id: moduleId, name: moduleName}; + + if (jsModule.clobbers.length > 0) { + moduleMetadata.clobbers = jsModule.clobbers.map(function(o) { return o.target; }); + } + if (jsModule.merges.length > 0) { + moduleMetadata.merges = jsModule.merges.map(function(o) { return o.target; }); + } + if (jsModule.runs) { + moduleMetadata.runs = true; + } + + modulesMetadata.push(moduleMetadata); + libraryRelease.require(path.join(pluginDir, jsModule.src), { expose: moduleId }); + }); + }); + + events.emit('verbose', 'Writing out cordova_plugins.js...'); + + // Create a stream and write plugin metadata into it + // instead of generating intermediate file on FS + var cordova_plugins = new Readable(); + cordova_plugins.push( + 'module.exports.metadata = ' + JSON.stringify(pluginMetadata, null, 4) + ';\n' + + 'module.exports = ' + JSON.stringify(modulesMetadata, null, 4) + ';\n', 'utf8'); + cordova_plugins.push(null); + + var bootstrap = new Readable(); + bootstrap.push('require(\'cordova/init\');\n', 'utf8'); + bootstrap.push(null); + + var moduleAliases = modulesMetadata + .reduce(function (accum, meta) { + accum['./' + meta.name] = meta.id; + return accum; + }, {}); + + libraryRelease + .add(cordova_plugins, {file: path.join(wwwDir, 'cordova_plugins.js'), expose: 'cordova/plugin_list'}) + .add(bootstrap) + .transform(aliasify, {aliases: moduleAliases}); + + var outReleaseFile = path.join(wwwDir, 'cordova.js'); + return generateFinalBundle(platform, libraryRelease, outReleaseFile, commitId, platformVersion); + }); +}; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/install.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/install.js b/cordova-lib/src/plugman/install.js index b99d155..071e851 100644 --- a/cordova-lib/src/plugman/install.js +++ b/cordova-lib/src/plugman/install.js @@ -471,7 +471,6 @@ function tryFetchDependency(dep, install, options) { dep.subdir = ''; return Q(url); }).fail(function(error){ -//console.log("Failed to resolve url='.': " + error); return Q(dep.url); }); @@ -565,35 +564,27 @@ function handleInstall(actions, pluginInfo, platform, project_dir, plugins_dir, // @tests - important this event is checked spec/install.spec.js events.emit('verbose', 'Install start for "' + pluginInfo.id + '" on ' + platform + '.'); - var handler = platform_modules.getPlatformProject(platform, project_dir); - var frameworkFiles = pluginInfo.getFrameworks(platform); // Frameworks are needed later - var pluginItems = pluginInfo.getFilesAndFrameworks(platform); - - // queue up native stuff - pluginItems.forEach(function(item) { - actions.push(actions.createAction(handler.getInstaller(item.itemType), - [item, plugin_dir, pluginInfo.id, options], - handler.getUninstaller(item.itemType), - [item, pluginInfo.id, options])); - }); - // run through the action stack - return actions.process(platform, project_dir) - .then(function(err) { - // queue up the plugin so prepare knows what to do. - var platformJson = PlatformJson.load(plugins_dir, platform); - platformJson.addInstalledPluginToPrepareQueue(pluginInfo.id, filtered_variables, options.is_top_level); - platformJson.save(); - // call prepare after a successful install - if (options.browserify) { - return plugman.prepareBrowserify(project_dir, platform, plugins_dir, options.www_dir, options.is_top_level, options.pluginInfoProvider); - } else { - return plugman.prepare(project_dir, platform, plugins_dir, options.www_dir, options.is_top_level, options.pluginInfoProvider); - } - }).then (function() { + options.variables = filtered_variables; + // Set up platform to install asset files/js modules to <platform>/platform_www dir + // instead of <platform>/www. This is required since on each prepare platform's www dir is changed + // and files from 'platform_www' merged into 'www'. Thus we need to persist these + // files platform_www directory, so they'll be applied to www on each prepare. + options.usePlatformWww = true; + + return platform_modules.getPlatformApi(platform, project_dir) + .addPlugin(pluginInfo, options) + .then (function() { events.emit('verbose', 'Install complete for ' + pluginInfo.id + ' on ' + platform + '.'); + // Add plugin to installed list. This already done in platform, + // but need to be duplicated here to manage dependencies properly. + PlatformJson.load(plugins_dir, platform) + .addPlugin(pluginInfo.id, filtered_variables, options.is_top_level) + .save(); + + if (platform == 'android' && semver.gte(options.platformVersion, '4.0.0-dev') && + pluginInfo.getFrameworks('platform').length > 0) { - if (platform == 'android' && semver.gte(options.platformVersion, '4.0.0-dev') && frameworkFiles.length > 0) { events.emit('verbose', 'Updating build files since android plugin contained <framework>'); var buildModule; try { http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/platforms/common.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/platforms/common.js b/cordova-lib/src/plugman/platforms/common.js index e3d3bcd..358ceed 100644 --- a/cordova-lib/src/plugman/platforms/common.js +++ b/cordova-lib/src/plugman/platforms/common.js @@ -79,7 +79,11 @@ module.exports = common = { }, // Sometimes we want to remove some java, and prune any unnecessary empty directories deleteJava:function(project_dir, destFile) { - var file = path.resolve(project_dir, destFile); + common.removeFileAndParents(project_dir, destFile, 'src'); + }, + removeFileAndParents:function(baseDir, destFile, stopper) { + stopper = stopper || '.'; + var file = path.resolve(baseDir, destFile); if (!fs.existsSync(file)) return; common.removeFileF(file); @@ -87,7 +91,7 @@ module.exports = common = { // check if directory is empty var curDir = path.dirname(file); - while(curDir !== path.resolve(project_dir, 'src')) { + while(curDir !== path.resolve(baseDir, stopper)) { if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { fs.rmdirSync(curDir); curDir = path.resolve(curDir, '..'); @@ -119,5 +123,27 @@ module.exports = common = { common.removeFile(www_dir, target); common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); } + }, + 'js-module': { + install: function (jsModule, plugin_dir, plugin_id, www_dir) { + // Copy the plugin's files into the www directory. + var moduleSource = path.resolve(plugin_dir, jsModule.src); + var moduleName = plugin_id + '.' + (jsModule.name || path.parse(jsModule.src).name); + + // Read in the file, prepend the cordova.define, and write it back out. + var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM + if (moduleSource.match(/.*\.json$/)) { + scriptContent = 'module.exports = ' + scriptContent; + } + scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) { ' + scriptContent + '\n});\n'; + + var moduleDestination = path.resolve(www_dir, 'plugins', plugin_id, jsModule.src); + shell.mkdir('-p', path.dirname(moduleDestination)); + fs.writeFileSync(moduleDestination, scriptContent, 'utf-8'); + }, + uninstall: function (jsModule, www_dir, plugin_id) { + var pluginRelativePath = path.join('plugins', plugin_id, jsModule.src); + common.removeFileAndParents(www_dir, pluginRelativePath); + } } }; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/plugman.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/plugman.js b/cordova-lib/src/plugman/plugman.js index 7d952f5..67637ed 100644 --- a/cordova-lib/src/plugman/plugman.js +++ b/cordova-lib/src/plugman/plugman.js @@ -65,8 +65,7 @@ var plugman = { addProperty(plugman, 'install', './install', true); addProperty(plugman, 'uninstall', './uninstall', true); addProperty(plugman, 'fetch', './fetch', true); -addProperty(plugman, 'prepare', './prepare'); -addProperty(plugman, 'prepareBrowserify', './prepare-browserify'); +addProperty(plugman, 'browserify', './browserify'); addProperty(plugman, 'help', './help'); addProperty(plugman, 'config', './config', true); addProperty(plugman, 'owner', './owner', true); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/prepare-browserify.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/prepare-browserify.js b/cordova-lib/src/plugman/prepare-browserify.js deleted file mode 100644 index 2dfb26a..0000000 --- a/cordova-lib/src/plugman/prepare-browserify.js +++ /dev/null @@ -1,214 +0,0 @@ -/** - 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. -*/ - -/* jshint unused:false, expr:true */ - -var platform_modules = require('../platforms/platforms'), - path = require('path'), - aliasify = require('aliasify'), - config_changes = require('./util/config-changes'), - common = require('./platforms/common'), - fs = require('fs'), - childProcess = require('child_process'), - shell = require('shelljs'), - util = require('util'), - events = require('../events'), - plugman = require('./plugman'), - bundle = require('cordova-js/tasks/lib/bundle-browserify'), - writeLicenseHeader = require('cordova-js/tasks/lib/write-license-header'), - Q = require('q'), - computeCommitId = require('cordova-js/tasks/lib/compute-commit-id'), - Readable = require('stream').Readable; - -var PlatformJson = require('./util/PlatformJson'); -var PluginInfoProvider = require('../PluginInfoProvider'); - -function uninstallQueuedPlugins(platformJson, wwwDir) { - // Check if there are any plugins queued for uninstallation, and if so, remove any of their plugin web assets loaded in - // via <js-module> elements - var plugins_to_uninstall = platformJson.root.prepare_queue.uninstalled; - if (plugins_to_uninstall && plugins_to_uninstall.length) { - var plugins_www = path.join(wwwDir, 'plugins'); - if (fs.existsSync(plugins_www)) { - plugins_to_uninstall.forEach(function(plug) { - var id = plug.id; - var plugin_modules = path.join(plugins_www, id); - if (fs.existsSync(plugin_modules)) { - events.emit('verbose', 'Removing plugins directory from www "'+plugin_modules+'"'); - shell.rm('-rf', plugin_modules); - } - }); - } - } -} - -function generateFinalBundle(platform, libraryRelease, outReleaseFile, commitId, platformVersion) { - var deferred = Q.defer(); - var outReleaseFileStream = fs.createWriteStream(outReleaseFile); - var time = new Date().valueOf(); - var symbolList = null; - - writeLicenseHeader(outReleaseFileStream, platform, commitId, platformVersion); - - var releaseBundle = libraryRelease.bundle(); - - releaseBundle.pipe(outReleaseFileStream); - - outReleaseFileStream.on('finish', function() { - var newtime = new Date().valueOf() - time; - plugman.emit('verbose', 'generated cordova.' + platform + '.js @ ' + commitId + ' in ' + newtime + 'ms'); - deferred.resolve(); - // TODO clean up all the *.browserify files - }); - - outReleaseFileStream.on('error', function(err) { - var newtime = new Date().valueOf() - time; - events.emit('log', 'error while generating cordova.js'); - deferred.reject(); - }); - return deferred.promise; -} - -function computeCommitIdSync() { - var deferred = Q.defer(); - computeCommitId(function(cId){ - deferred.resolve(cId); - }); - return deferred.promise; -} - -function getPlatformVersion(cId, project_dir) { - var deferred = Q.defer(); - //run version script for each platform to get platformVersion - var versionPath = path.join(project_dir, '/cordova/version'); - childProcess.exec('"' + versionPath + '"', function(err, stdout, stderr) { - if (err) { - events.emit('log', 'Error running platform version script'); - events.emit('log', err); - deferred.resolve('N/A'); - } else { - deferred.resolve(stdout.trim()); - } - }); - return deferred.promise; -} - -// 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.). -module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_dir, is_top_level, pluginInfoProvider) { - // Process: - // - Do config munging by calling into config-changes module - // - 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/cordova_plugins.json. - // This file is not really used. Maybe cordova app harness - events.emit('verbose', 'Preparing ' + platform + ' browserify project'); - pluginInfoProvider = pluginInfoProvider || new PluginInfoProvider(); // Allow null for backwards-compat. - var platformJson = PlatformJson.load(plugins_dir, platform); - var wwwDir = www_dir || platform_modules.getPlatformProject(platform, project_dir).www_dir(); - - uninstallQueuedPlugins(platformJson, www_dir); - - events.emit('verbose', 'Processing configuration changes for plugins.'); - config_changes.process(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider); - - if(!is_top_level) { - return Q(); - } - - var commitId; - return computeCommitIdSync() - .then(function(cId){ - commitId = cId; - return getPlatformVersion(commitId, project_dir); - }).then(function(platformVersion){ - var libraryRelease = bundle(platform, false, commitId, platformVersion); - - var pluginMetadata = {}; - var modulesMetadata = []; - - var plugins = Object.keys(platformJson.root.installed_plugins).concat(Object.keys(platformJson.root.dependent_plugins)); - events.emit('verbose', 'Iterating over installed plugins:', plugins); - plugins && plugins.forEach(function(plugin) { - var pluginDir = path.join(plugins_dir, plugin); - var pluginInfo = pluginInfoProvider.get(pluginDir); - // pluginMetadata is a mapping from plugin IDs to versions. - pluginMetadata[pluginInfo.id] = pluginInfo.version; - - // Copy www assets described in <asset> tags. - pluginInfo.getAssets(platform) - .forEach(function(asset) { - common.asset.install(asset, pluginDir, wwwDir); - }); - - pluginInfo.getJsModules(platform) - .forEach(function(jsModule) { - var moduleName = jsModule.name ? jsModule.name : path.basename(jsModule.src, '.js'); - var moduleId = pluginInfo.id + '.' + moduleName; - var moduleMetadata = {file: jsModule.src, id: moduleId, name: moduleName}; - - if (jsModule.clobbers.length > 0) { - moduleMetadata.clobbers = jsModule.clobbers.map(function(o) { return o.target; }); - } - if (jsModule.merges.length > 0) { - moduleMetadata.merges = jsModule.merges.map(function(o) { return o.target; }); - } - if (jsModule.runs) { - moduleMetadata.runs = true; - } - - modulesMetadata.push(moduleMetadata); - libraryRelease.require(path.join(pluginDir, jsModule.src), { expose: moduleId }); - }); - }); - - events.emit('verbose', 'Writing out cordova_plugins.js...'); - - // Create a stream and write plugin metadata into it - // instead of generating intermediate file on FS - var cordova_plugins = new Readable(); - cordova_plugins.push( - 'module.exports.metadata = ' + JSON.stringify(pluginMetadata, null, 4) + ';\n' + - 'module.exports = ' + JSON.stringify(modulesMetadata, null, 4) + ';\n', 'utf8'); - cordova_plugins.push(null); - - var bootstrap = new Readable(); - bootstrap.push('require(\'cordova/init\');\n', 'utf8'); - bootstrap.push(null); - - var moduleAliases = modulesMetadata - .reduce(function (accum, meta) { - accum['./' + meta.name] = meta.id; - return accum; - }, {}); - - libraryRelease - .add(cordova_plugins, {file: path.join(wwwDir, 'cordova_plugins.js'), expose: 'cordova/plugin_list'}) - .add(bootstrap) - .transform(aliasify, {aliases: moduleAliases}); - - var outReleaseFile = path.join(wwwDir, 'cordova.js'); - return generateFinalBundle(platform, libraryRelease, outReleaseFile, commitId, platformVersion); - }); -}; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/prepare.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/prepare.js b/cordova-lib/src/plugman/prepare.js deleted file mode 100644 index c52d92e..0000000 --- a/cordova-lib/src/plugman/prepare.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - 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. -*/ - -/* jshint expr:true, quotmark:false */ - -var platform_modules = require('../platforms/platforms'), - path = require('path'), - config_changes = require('./util/config-changes'), - common = require('./platforms/common'), - fs = require('fs'), - shell = require('shelljs'), - Q = require('q'), - events = require('../events'); -var PlatformJson = require('./util/PlatformJson'); -var PluginInfoProvider = require('../PluginInfoProvider'); - -// 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.). -module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_dir, is_top_level, pluginInfoProvider) { - // Process: - // - Do config munging by calling into config-changes module - // - 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/cordova_plugins.json. - // - Cordova.js contains code to load them at runtime from that file. - events.emit('verbose', 'Preparing ' + platform + ' project'); - pluginInfoProvider = pluginInfoProvider || new PluginInfoProvider(); // Allow null for backwards-compat. - var platformJson = PlatformJson.load(plugins_dir, platform); - var wwwDir = www_dir || platform_modules.getPlatformProject(platform, project_dir).www_dir(); - - // Check if there are any plugins queued for uninstallation, and if so, remove any of their plugin web assets loaded in - // via <js-module> elements - var plugins_to_uninstall = platformJson.root.prepare_queue.uninstalled; - if (plugins_to_uninstall && plugins_to_uninstall.length) { - var plugins_www = path.join(wwwDir, 'plugins'); - if (fs.existsSync(plugins_www)) { - plugins_to_uninstall.forEach(function(plug) { - var id = plug.id; - var plugin_modules = path.join(plugins_www, id); - if (fs.existsSync(plugin_modules)) { - events.emit('verbose', 'Removing plugins directory from www "'+plugin_modules+'"'); - shell.rm('-rf', plugin_modules); - } - }); - } - } - - events.emit('verbose', 'Processing configuration changes for plugins.'); - config_changes.process(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider); - - // This array holds all the metadata for each module and ends up in cordova_plugins.json - var plugins = Object.keys(platformJson.root.installed_plugins).concat(Object.keys(platformJson.root.dependent_plugins)); - var moduleObjects = []; - var pluginMetadata = {}; - events.emit('verbose', 'Iterating over installed plugins:', plugins); - - plugins && plugins.forEach(function(plugin) { - var pluginDir = path.join(plugins_dir, plugin); - var pluginInfo = pluginInfoProvider.get(pluginDir); - - var plugin_id = pluginInfo.id; - // pluginMetadata is a mapping from plugin IDs to versions. - pluginMetadata[plugin_id] = pluginInfo.version; - - // add the plugins dir to the platform's www. - var platformPluginsDir = path.join(wwwDir, 'plugins'); - // XXX this should not be here if there are no js-module. It leaves an empty plugins/ directory - shell.mkdir('-p', platformPluginsDir); - - var jsModules = pluginInfo.getJsModules(platform); - var assets = pluginInfo.getAssets(platform); - - // Copy www assets described in <asset> tags. - assets.forEach(function(asset) { - common.asset.install(asset, pluginDir, wwwDir); - }); - - jsModules.forEach(function(module) { - // Copy the plugin's files into the www directory. - // NB: We can't always use path.* functions here, because they will use platform slashes. - // But the path in the plugin.xml and in the cordova_plugins.js should be always forward slashes. - var pathParts = module.src.split('/'); - - var fsDirname = path.join.apply(path, pathParts.slice(0, -1)); - var fsDir = path.join(platformPluginsDir, plugin_id, fsDirname); - shell.mkdir('-p', fsDir); - - // Read in the file, prepend the cordova.define, and write it back out. - var moduleName = plugin_id + '.'; - if (module.name) { - moduleName += module.name; - } else { - var result = module.src.match(/([^\/]+)\.js/); - moduleName += result[1]; - } - - var fsPath = path.join.apply(path, pathParts); - var scriptContent = fs.readFileSync(path.join(pluginDir, fsPath), 'utf-8').replace(/^\ufeff/, ''); // Window BOM - if (fsPath.match(/.*\.json$/)) { - scriptContent = 'module.exports = ' + scriptContent; - } - scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) { ' + scriptContent + '\n});\n'; - fs.writeFileSync(path.join(platformPluginsDir, plugin_id, fsPath), scriptContent, 'utf-8'); - - // Prepare the object for cordova_plugins.json. - var obj = { - file: ['plugins', plugin_id, module.src].join('/'), - id: moduleName - }; - if (module.clobbers.length > 0) { - obj.clobbers = module.clobbers.map(function(o) { return o.target; }); - } - if (module.merges.length > 0) { - obj.merges = module.merges.map(function(o) { return o.target; }); - } - if (module.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 wrapped in a cordova module to cordova_plugins.js - var final_contents = "cordova.define('cordova/plugin_list', function(require, exports, module) {\n"; - final_contents += 'module.exports = ' + JSON.stringify(moduleObjects,null,' ') + ';\n'; - final_contents += 'module.exports.metadata = \n'; - final_contents += '// TOP OF METADATA\n'; - final_contents += JSON.stringify(pluginMetadata, null, ' ') + '\n'; - final_contents += '// BOTTOM OF METADATA\n'; - final_contents += '});'; // Close cordova.define. - - events.emit('verbose', 'Writing out cordova_plugins.js...'); - fs.writeFileSync(path.join(wwwDir, 'cordova_plugins.js'), final_contents, 'utf-8'); - - return Q(); -}; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/uninstall.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/uninstall.js b/cordova-lib/src/plugman/uninstall.js index 9fdc2e4..1bc1e2f 100644 --- a/cordova-lib/src/plugman/uninstall.js +++ b/cordova-lib/src/plugman/uninstall.js @@ -30,7 +30,6 @@ var path = require('path'), Q = require('q'), events = require('../events'), platform_modules = require('../platforms/platforms'), - plugman = require('./plugman'), promiseutil = require('../util/promise-util'), HooksRunner = require('../hooks/HooksRunner'), cordovaUtil = require('../cordova/util'); @@ -299,49 +298,23 @@ function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugin // Returns a promise. function handleUninstall(actions, platform, pluginInfo, project_dir, www_dir, plugins_dir, is_top_level, options) { - var plugin_id = pluginInfo.id; - var plugin_dir = pluginInfo.dir; - var handler = platform_modules.getPlatformProject(platform, project_dir); - www_dir = www_dir || handler.www_dir(); - events.emit('log', 'Uninstalling ' + plugin_id + ' from ' + platform); - - var pluginItems = pluginInfo.getFilesAndFrameworks(platform); - var assets = pluginInfo.getAssets(platform); - var frameworkFiles = pluginInfo.getFrameworks(platform); - - // queue up native stuff - pluginItems.forEach(function(item) { - // CB-5238 Don't uninstall non custom frameworks. - if (item.itemType == 'framework' && !item.custom) return; - actions.push(actions.createAction(handler.getUninstaller(item.itemType), - [item, plugin_id, options], - handler.getInstaller(item.itemType), - [item, plugin_dir, plugin_id, options])); - }); - - // queue up asset uninstallation - var common = require('./platforms/common'); - assets.forEach(function(asset) { - actions.push(actions.createAction(common.asset.uninstall, [asset, www_dir, plugin_id], common.asset.install, [asset, plugin_dir, www_dir])); - }); + events.emit('log', 'Uninstalling ' + pluginInfo.id + ' from ' + platform); - // run through the action stack - return actions.process(platform, project_dir) + // Set up platform to uninstall asset files/js modules + // from <platform>/platform_www dir instead of <platform>/www. + options.usePlatformWww = true; + return platform_modules.getPlatformApi(platform, project_dir) + .removePlugin(pluginInfo, options) .then(function() { - // WIN! - events.emit('verbose', plugin_id + ' uninstalled from ' + platform + '.'); - // queue up the plugin so prepare can remove the config changes - var platformJson = PlatformJson.load(plugins_dir, platform); - platformJson.addUninstalledPluginToPrepareQueue(plugin_id, is_top_level); - platformJson.save(); - // call prepare after a successful uninstall - if (options.browserify) { - return plugman.prepareBrowserify(project_dir, platform, plugins_dir, www_dir, is_top_level, options.pluginInfoProvider); - } else { - return plugman.prepare(project_dir, platform, plugins_dir, www_dir, is_top_level, options.pluginInfoProvider); - } - }).then(function() { - if (platform == 'android' && semver.gte(options.platformVersion, '4.0.0-dev') && frameworkFiles.length > 0) { + // Remove plugin from installed list. This already done in platform, + // but need to be duplicated here to remove plugin entry from project's + // plugin list to manage dependencies properly. + PlatformJson.load(plugins_dir, platform) + .removePlugin(pluginInfo.id, is_top_level) + .save(); + + if (platform == 'android' && semver.gte(options.platformVersion, '4.0.0-dev') && + pluginInfo.getFrameworks(platform).length > 0) { events.emit('verbose', 'Updating build files since android plugin contained <framework>'); var buildModule; try { http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/util/ConfigFile.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/util/ConfigFile.js b/cordova-lib/src/plugman/util/ConfigFile.js index c317668..15191ad 100644 --- a/cordova-lib/src/plugman/util/ConfigFile.js +++ b/cordova-lib/src/plugman/util/ConfigFile.js @@ -69,7 +69,7 @@ function ConfigFile_load() { self.data = xml_helpers.parseElementtreeSync(filepath); } else if (ext == '.pbxproj') { self.type = 'pbxproj'; - var projectFile = platforms.getPlatformProject('ios', self.project_dir).parseProjectFile(self.project_dir); + var projectFile = platforms.getPlatformApi('ios', self.project_dir)._handler.parseProjectFile(self.project_dir); self.data = projectFile.xcode; self.cordovaVersion = projectFile.cordovaVersion; } else { http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/util/PlatformJson.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/util/PlatformJson.js b/cordova-lib/src/plugman/util/PlatformJson.js index 1b9f05b..318a5d9 100644 --- a/cordova-lib/src/plugman/util/PlatformJson.js +++ b/cordova-lib/src/plugman/util/PlatformJson.js @@ -81,6 +81,26 @@ PlatformJson.prototype.isPluginInstalled = function(pluginId) { this.isPluginDependent(pluginId); }; +PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) { + var pluginsList = isTopLevel ? + this.root.installed_plugins : + this.root.dependent_plugins; + + pluginsList[pluginId] = variables; + + return this; +}; + +PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) { + var pluginsList = isTopLevel ? + this.root.installed_plugins : + this.root.dependent_plugins; + + delete pluginsList[pluginId]; + + return this; +}; + PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level) { this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level}); }; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/util/action-stack.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/util/action-stack.js b/cordova-lib/src/plugman/util/action-stack.js index c8bd21b..c1c2a4e 100644 --- a/cordova-lib/src/plugman/util/action-stack.js +++ b/cordova-lib/src/plugman/util/action-stack.js @@ -19,8 +19,7 @@ /* jshint quotmark:false */ -var platforms = require("../../platforms/platforms"), - events = require('../../events'), +var events = require('../../events'), Q = require('q'); function ActionStack() { @@ -47,22 +46,11 @@ ActionStack.prototype = { // Returns a promise. process:function(platform, project_dir) { events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...'); - var project_files; - - // parse platform-specific project files once - var platformProject = platforms.getPlatformProject(platform, project_dir); - if (platformProject.parseProjectFile) { - events.emit('verbose', 'Parsing ' + platform + ' project files...'); - project_files = platformProject.parseProjectFile(project_dir); - } while (this.stack.length) { var action = this.stack.shift(); var handler = action.handler.run; var action_params = action.handler.params; - if (project_files) { - action_params.push(project_files); - } try { handler.apply(null, action_params); @@ -76,10 +64,6 @@ ActionStack.prototype = { var revert = undo.reverter.run; var revert_params = undo.reverter.params; - if (project_files) { - revert_params.push(project_files); - } - try { revert.apply(null, revert_params); } catch(err) { @@ -94,10 +78,6 @@ ActionStack.prototype = { } events.emit('verbose', 'Action stack processing complete.'); - if (project_files) { - events.emit('verbose', 'Writing out ' + platform + ' project files...'); - project_files.write(); - } return Q(); } }; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07271a5c/cordova-lib/src/plugman/util/config-changes.js ---------------------------------------------------------------------- diff --git a/cordova-lib/src/plugman/util/config-changes.js b/cordova-lib/src/plugman/util/config-changes.js index 63c3ec3..ff23e2a 100644 --- a/cordova-lib/src/plugman/util/config-changes.js +++ b/cordova-lib/src/plugman/util/config-changes.js @@ -54,8 +54,8 @@ var keep_these_frameworks = [ exports.PlatformMunger = PlatformMunger; exports.process = function(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) { - var munger = new PlatformMunger(platform, project_dir, plugins_dir, platformJson, pluginInfoProvider); - munger.process(); + var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider); + munger.process(plugins_dir); munger.save_all(); }; @@ -65,12 +65,10 @@ exports.process = function(plugins_dir, project_dir, platform, platformJson, plu * Can deal with config file of a single project. * Parsed config files are cached in a ConfigKeeper object. ******************************************************************************/ -function PlatformMunger(platform, project_dir, plugins_dir, platformJson, pluginInfoProvider) { +function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) { checkPlatform(platform); this.platform = platform; this.project_dir = project_dir; - this.plugins_dir = plugins_dir; - this.platform_handler = platforms.getPlatformProject(platform, project_dir); this.config_keeper = new ConfigKeeper(project_dir); this.platformJson = platformJson; this.pluginInfoProvider = pluginInfoProvider; @@ -130,14 +128,15 @@ function PlatformMunger_apply_file_munge(file, munge, remove) { PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes; -function remove_plugin_changes(plugin_name, plugin_id, is_top_level) { +function remove_plugin_changes(pluginInfo, is_top_level) { var self = this; var platform_config = self.platformJson.root; - var plugin_dir = path.join(self.plugins_dir, plugin_name); - var plugin_vars = (is_top_level ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]); + var plugin_vars = is_top_level ? + platform_config.installed_plugins[pluginInfo.id] : + platform_config.dependent_plugins[pluginInfo.id]; // get config munge, aka how did this plugin change various config files - var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars); + var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars); // global munge looks at all plugins' changes to config files var global_munge = platform_config.config_munge; var munge = mungeutil.decrement_munge(global_munge, config_munge); @@ -147,7 +146,7 @@ function remove_plugin_changes(plugin_name, plugin_id, is_top_level) { // TODO: remove this check and <plugins-plist> sections in spec/plugins/../plugin.xml files. events.emit( 'warn', - 'WARNING: Plugin "' + plugin_id + '" uses <plugins-plist> element(s), ' + + 'WARNING: Plugin "' + pluginInfo.id + '" uses <plugins-plist> element(s), ' + 'which are no longer supported. Support has been removed as of Cordova 3.4.' ); continue; @@ -168,22 +167,18 @@ function remove_plugin_changes(plugin_name, plugin_id, is_top_level) { } // Remove from installed_plugins - if (is_top_level) { - delete platform_config.installed_plugins[plugin_id]; - } else { - delete platform_config.dependent_plugins[plugin_id]; - } + self.platformJson.removePlugin(pluginInfo.id, is_top_level); + return self; } PlatformMunger.prototype.add_plugin_changes = add_plugin_changes; -function add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment) { +function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increment) { var self = this; var platform_config = self.platformJson.root; - var plugin_dir = path.join(self.plugins_dir, plugin_id); // get config munge, aka how should this plugin change various config files - var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars); + var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars); // global munge looks at all plugins' changes to config files // TODO: The should_increment param is only used by cordova-cli and is going away soon. @@ -203,7 +198,7 @@ function add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increme if (file == 'plugins-plist' && self.platform == 'ios') { events.emit( 'warn', - 'WARNING: Plugin "' + plugin_id + '" uses <plugins-plist> element(s), ' + + 'WARNING: Plugin "' + pluginInfo.id + '" uses <plugins-plist> element(s), ' + 'which are no longer supported. Support has been removed as of Cordova 3.4.' ); continue; @@ -222,12 +217,9 @@ function add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increme self.apply_file_munge(file, munge.files[file]); } - // Move to installed_plugins if it is a top-level plugin - if (is_top_level) { - platform_config.installed_plugins[plugin_id] = plugin_vars || {}; - } else { - platform_config.dependent_plugins[plugin_id] = plugin_vars || {}; - } + // Move to installed/dependent_plugins + self.platformJson.addPlugin(pluginInfo.id, plugin_vars || {}, is_top_level); + return self; } @@ -253,24 +245,19 @@ function reapply_global_munge () { self.apply_file_munge(file, global_munge.files[file]); } + + return self; } // generate_plugin_config_munge // Generate the munge object from plugin.xml + vars PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge; -function generate_plugin_config_munge(plugin_dir, vars) { +function generate_plugin_config_munge(pluginInfo, vars) { var self = this; vars = vars || {}; - // Add PACKAGE_NAME variable into vars - if (!vars['PACKAGE_NAME']) { - vars['PACKAGE_NAME'] = self.platform_handler.package_name(self.project_dir); - } - var munge = { files: {} }; - var pluginInfo = self.pluginInfoProvider.get(plugin_dir); - var changes = pluginInfo.getConfigFiles(self.platform); // note down pbxproj framework munges in special section of munge obj @@ -283,10 +270,14 @@ function generate_plugin_config_munge(plugin_dir, vars) { } }); } - + // Demux 'package.appxmanifest' into relevant platform-specific appx manifests. // Only spend the cycles if there are version-specific plugin settings - if (self.platform === 'windows' && changes.some(function(change) { return ((typeof change.versions !== 'undefined') || (typeof change.deviceTarget !== 'undefined')); })) + if (self.platform === 'windows' && + changes.some(function(change) { + return ((typeof change.versions !== 'undefined') || + (typeof change.deviceTarget !== 'undefined')); + })) { var manifests = { 'windows': { @@ -334,7 +325,7 @@ function generate_plugin_config_munge(plugin_dir, vars) { // at this point, 'change' targets package.appxmanifest and has a version attribute knownWindowsVersionsForTargetDeviceSet.forEach(function(winver) { - // This is a local function that creates the new replacement representing the + // This is a local function that creates the new replacement representing the // mutation. Used to save code further down. var createReplacement = function(manifestFile, originalChange) { var replacement = { @@ -389,18 +380,20 @@ function generate_plugin_config_munge(plugin_dir, vars) { // Go over the prepare queue and apply the config munges for each plugin // that has been (un)installed. PlatformMunger.prototype.process = PlatformMunger_process; -function PlatformMunger_process() { +function PlatformMunger_process(plugins_dir) { var self = this; var platform_config = self.platformJson.root; // Uninstallation first platform_config.prepare_queue.uninstalled.forEach(function(u) { - self.remove_plugin_changes(u.plugin, u.id, u.topLevel); + var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin)); + self.remove_plugin_changes(pluginInfo, u.topLevel); }); // Now handle installation platform_config.prepare_queue.installed.forEach(function(u) { - self.add_plugin_changes(u.plugin, u.vars, u.topLevel, true); + var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin)); + self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true); }); // Empty out installed/ uninstalled queues. --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org