plugman
A command line tool to distribute and package plugins for use with Apache Cordova, nee PhoneGap.
This document defines tool usage and the plugin format specification.
Design Goals
- Facilitate programmatic installation and manipulation of plugins
- Detail the dependencies and components of individual plugins
- Allow code reuse between different target platforms
Usage
plugman --fetch --plugin <directory|git-url|name> [--plugins_dir <directory>]
plugman --install --platform <ios|android|bb10> --project <directory> --plugin <name> [--plugins_dir <directory>]
plugman --uninstall --platform <ios|android|bb10> --project <directory> --plugin <name> [--plugins_dir <directory>]
plugman --remove --plugin <name> [--plugins_dir <directory>]
plugman --list [--plugins_dir <directory>]
plugman --prepare --platform <ios|android|bb10> --project <directory> [--plugins_dir <directory>]
--fetch
: Retrieves and stores a plugin--install
: Installs an already---fetch
'ed plugin into a cordova project--uninstall
: Uninstalls an already---install
'ed plugin from a cordova project--remove
: Removes a --fetch
'ed plugin--list
: Lists all --fetch
'ed plugins--prepare
: Based on all installed plugins, will set up properly injecting plugin JavaScript code and setting up permissions properly. Implicitly called after --install
and --uninstall
commands. See below for more details.
--plugins_dir
defaults to <project>/cordova/plugins
, but can be any directory containing a subdirectory for each fetched plugin
Note that --fetch
and --remove
deal with the local cache of the plugin's files and don't care about platforms, while --install
and --uninstall
require specifying the target platform and the location of the project, and actually do installation of plugin code and assets.
Supported Platforms
Example Plugins
The plugins found https://github.com/MobileChromeApps/chrome-cordova/plugins are maintained actively by a contributor to plugman, and should serve as good examples.
Development
Basic installation:
git clone https://git-wip-us.apache.org/repos/asf/cordova-plugman.git
cd cordova-plugman
npm install -g
Linking the global executable to the git repo:
git clone https://git-wip-us.apache.org/repos/asf/cordova-plugman.git
cd cordova-plugman
npm install
sudo npm link
Plugin Directory Structure
A plugin is typically a combination of some web/www code, and some native code.
However, plugins may have only one of these things - a plugin could be a single
JavaScript file, or some native code with no corresponding JavaScript.
Here is a sample plugin named foo with android and ios platforms support, and 2 www assets.
foo-plugin/
|- plugin.xml # xml-based manifest
|- src/ # native source for each platform
| |- android/
| | `- Foo.java
| `- ios/
| |- CDVFoo.h
| `- CDVFoo.m
|- README.md
`- www/
|- foo.js
`- foo.png
This structure is suggested, but not required.
plugin.xml Manifest Format
Last edited April 17 2013.
The plugin.xml
file is an XML document in the plugins namespace -
http://apache.org/cordova/ns/plugins/1.0
. It contains a top-level plugin
element defining the plugin, and children that define the structure of the
plugin.
A sample plugin element:
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="com.alunny.foo"
version="1.0.2">
<plugin> element
The plugin
element is the top-level element of the plugin manifest. It has the
following attributes:
xmlns (required)
The plugin namespace - http://apache.org/cordova/ns/plugins/1.0
. If the document
contains XML from other namespaces - for example, tags to be added to the
AndroidManifest.xml file - those namespaces should also be included in the
top-level element.
id (required)
A reverse-domain style identifier for the plugin - for example, com.alunny.foo
version (required)
A version number for the plugin, that matches the following major-minor-patch
style regular expression:
^\d+[.]\d+[.]\d+$
<engines> and <engine> elements
The child elements of the <engines>
element specify versions of
Apache Cordova-based frameworks that this plugin supports. An example:
<engines>
<engine name="cordova" version="1.7.0" />
<engine name="cordova" version="1.8.1" />
<engine name="worklight" version="1.0.0" />
</engines>
Similarly to the version
attribute for the <plugin>
element,
the version string specified should match a major-minor-patch string
conforming to the regular expression:
^\d+[.]\d+[.]\d+$
Engine elements may also have fuzzy matches to avoid repetition, and reduce
maintenance when the underlying platform is updated. A minimum of >
, >=
,
<
and <=
should be supported by tools, such as:
<engines>
<engine name="cordova" version=">=1.7.0" />
<engine name="cordova" version="<1.8.1" />
</engines>
plugman will abort plugin installation if the target project does not meet the engine constraints, and exit with a non-zero code.
If no <engine>
tags are specified, plugman will attempt to install into the specified cordova project directory blindly.
<name> element
A human-readable name for the plugin. The text content of the element contains
the name of the plugin. An example:
<name>Foo</name>
This element does not (yet) handle localization.
<asset> element
One or more elements listing the files or directories to be copied into a
Cordova app's www
directory. A couple of samples:
<!-- a single file, to be copied in the root directory -->
<asset src="www/foo.js" target="foo.js" />
<!-- a directory, also to be copied in the root directory -->
<asset src="www/foo" target="foo" />
All assets tags require both a src
attribute and a target
attribute.
Web-only plugins would contains mainly <asset> elements.
src (required)
Where the file or directory is located in the plugin package, relative to the
plugin.xml
document.
If a file does not exist at the source location, plugman will stop/reverse the installation process and notify the user, and exit with a non-zero code.
target (required)
Where the file or directory should be located in the Cordova app, relative to
the www
directory.
Assets can be targeted to subdirectories - for instance:
<asset src="www/new-foo.js" target="js/experimental/foo.js" />
would create the js/experimental
directory in the www
directory, if not
present, and then copy the file new-foo.js
as foo.js
into that directory.
If a file exists at the target location, plugman will stop/reverse the installation process and notify the user of the conflict, and exit with a non-zero code.
<js-module>
element
A typical plugin includes one or more JavaScript files. Rather than have the user of your plugin add <script>
tags for your JavaScript to their HTML file(s) manually, you should use <js-module>
tags for your Javascript files.
<asset>
tags are a dumb copy: copy a file from the plugin subdirectory to www
.
In contrast, <js-module>
tags are much more sophisticated. They look like this:
<js-module src="socket.js" name="Socket">
<clobbers target="chrome.socket" />
</js-module>
With the above example, a call to plugman --prepare
will copy socket.js to www/plugins/my.plugin.id/socket.js
. Further, it will add an entry for this plugin to www/cordova_plugins.json
. At load time, code in cordova.js
will use an XHR to read this file, inject a <script>
tag for each Javascript file, and add a mapping to clobber or merge as appropriate (see below).
DO NOT wrap the file with cordova.define
; this will be added automatically. Your module will be wrapped in a closure, and will have module
, exports
and require
in scope, as normal for AMD modules.
Details for the <js-module>
tag:
- The
src
points to a file in the plugin directory relative to the plugin.xml
file. - The
name
gives the last part of the module name. It can generally be whatever you like, and it only matters if you want to use cordova.require
to import other parts of your plugins in your JavaScript code. The module name for a <js-module>
is your plugin's id
followed by the value of name
. For the example above, with an id
of chrome.socket
, the module name is chrome.socket.Socket
. - Inside the
<js-module>
tag there are three legal sub-tags:
<clobbers target="some.value" />
indicates that the module.exports
will be inserted into the window
object as window.some.value
. You can have as many <clobbers>
as you like. If the object(s) does not exist on window
, they will be created.<merges target="some.value" />
indicates that your module should be merged with any existing value at window.some.value
. If any key already exists, you module's version overrides the original. You can have as many <merges>
as you like. If the object(s) does not exist on window
, they will be created.<runs />
means that your code should be cordova.require
d, but not installed on the window
object anywhere. This is useful for initializing the module, attaching event handlers or otherwise. You can only have 0 or 1 <runs />
tags. Note that including a <runs />
with <clobbers />
or <merges />
is redundant, since they also cordova.require
your module.- An empty
<js-module>
will still be loaded and can be cordova.require
d in other modules.
If src
does not resolve to a file that can be found, plugman will stop/reverse the installation, notify the user of the problem and exit with a non-zero code.
<platform>
Platform tags identify platforms that have associated native code and/or require configuration file modifications. Tools using
this specification can identify supported platforms and install the code into
Cordova projects.
Plugins without <platform>
tags are assumed to be JS-only, and therefore installable on any and all platforms.
A sample platform tag:
<platform name="android">
<!-- android specific elements -->
</platform>
<platform name="ios">
<!-- ios specific elements -->
</platform>
name (required)
The name
attribute identifies a platform as supported - it also associates the
element's children with that platform.
Platform names should be all-lowercase. Platform names, as arbitrarily chosen,
are listed:
- android
- bb10
- ios
- wp7
- wp8
- windows8
<source-file>
source-file
elements identify executable source code that should be installed
into a project. A couple of examples:
<!-- android -->
<source-file src="src/android/Foo.java"
target-dir="src/com/alunny/foo" />
<!-- ios -->
<source-file src="src/ios/CDVFoo.m" />
src (required)
Where the file is located, relative to the plugin.xml
file.
If src
does not resolve to a file that can be found, plugman will stop/reverse the installation, notify the user of the problem and exit with a non-zero code.
target-dir
A directory where the files should be copied into, relative to the root of the
Cordova project.
In practice, this is most important for Java-based platforms, where a file in
the package com.alunny.foo
has be located under the directory
com/alunny/foo
. For platforms where the source directory is not important,
plugin authors should omit this attribute.
As with assets, if a source-file
's target
would overwrite an existing file, plugman will stop/reverse the installation, notify the user and exit with a non-zero code.
<config-file>
Identifies an XML-based configuration file to be modified, where in that
document the modification should take place, and what should be modified.
The config-file
element only allows for appending
new children into an XML document. The children are XML literals that are the
to be inserted in the target document.
Example:
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<activity android:name="com.foo.Foo"
android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>
</config-file>
target
The file to be modified, and the path relative to the root of the Cordova
project.
If this file does not exist, the tool should stop/reverse the installation process, warn the user, and exit with a non-zero code.
parent
An absolute XPath selector pointing to the parent of the elements to be added to
the config file.
If the selector does not resolve to a child of the specified document, the tool should stop/reverse the installation process, warn the user, and exit with a non-zero code.
<plugins-plist>
This is OUTDATED as it only applies to cordova-ios 2.2.0 and below. Use <config-file> tag (same as Android) for newer versions of Cordova.
Example:
<config-file target="config.xml" parent="/cordova/plugins">
<plugin name="ChildBrowser"
value="ChildBrowserCommand"/>
</config-file>
Specifies a key and value to append to the correct AppInfo.plist
file in an
iOS Cordova project. Example:
<plugins-plist key="Foo"
string="CDVFoo" />
<resource-file> and <header-file>
Like source files, but specifically for platforms that distinguish between
source files, headers, and resources (iOS)
Examples:
<resource-file src="CDVFoo.bundle" />
<resource-file src="CDVFooViewController.xib" />
<header-file src="CDVFoo.h" />
<framework>
Identifies a framework (usually part of the OS/platform) that the plugin depends on.
Examples:
<framework src="libsqlite3.dylib" />
<framework src="social.framework" weak="true" />
plugman identifies the framework through the src
attribute and attempts to add the framework to the Cordova project, in the correct fashion for a given platform.
The optional weak
attribute is a boolean denoting whether the framework should be weakly-linked. Default is false
.
<dependencies> and <dependency>
Identifies dependency of the plugin on another plugin.
Example:
<dependencies>
<dependency name="com.facebook.plugin" url="http://plugins.cordova.io" />
<dependency name="com.adobe.omniture" url="http://plugins.phonegap.com" version="1.0.0" />
</dependencies>
Dependencies denote other plugins that must be installed in order for the current plugin to work properly.
Two different versions of the same plugin cannot be installed into the same application. If the user attempts to do so, the tooling should error out appropriately and exit with a non-zero code.
Dependent plugins should be fetched first and stored in the user's specified plugins directory (or the default enforced by the tool if none is specified). These plugins are then installed into the user's cordova project, one by one, in a recursive fashion.
If installation of any dependent plugin is not successful, the tool should not proceed with installation of the parent plugin and reverse all changes made to the cordova project by the installation.
name
A reverse-style domain identifier, as specified by a plugin in the top-level plugin
element's id
attribute.
url
URL pointing to a valid cordova plugin "universe."
The tooling should retrieve plugin contents based on a combination of this URL and the name
attribute, i.e. url
+ '/' + name
.
If the retrieval URL does not resolve or does not contain a valid plugin.xml file, the tool should error out and exit with a non-zero code.
version
Optionally you can specify a version requirement for the plugin dependency. Identical syntax for the <engine>
element's version
attribute above should be employed.
<info>
The tool will provide additional information to users. This is useful when you require some extra steps that can't be easily automated or are out of the scope of plugman.
Examples:
You need to install **Google Play Services** from the `Android Extras` section using the Android SDK manager (run `android`).
You need to add the following line to your local.properties
android.library.reference.1=PATH_TO_ANDROID_SDK/sdk/extras/google/google_play_services/libproject/google-play-services_lib
Variables
In certain cases, a plugin may need to make configuration changes dependent on
the target application. For example, to register for C2DM on Android, an app
with package id com.alunny.message
would need a permission like:
<uses-permission
android:name="com.alunny.message.permission.C2D_MESSAGE"/>
In cases like this (where the content inserted from the plugin.xml
file is
not known ahead of time), variables can be indicated by a dollar-sign and a
series of capital letters, digits and underscores. For the above example, the
plugin.xml
file would include this tag:
<uses-permission
android:name="$PACKAGE_NAME.permission.C2D_MESSAGE"/>
plugman replaces variable references with the
correct value, if specified, or the empty string otherwise. The value of the
variable reference may be detected (in this case, from the AndroidManifest.xml
file), or specified by the user of the tool; the exact process is dependent on
the particular tool.
plugman can request users to specify variables required by a plugin. For example API keys for C2M and Google Maps can be specified as a command line argument like so:
plugman --platform ios|android --project /path/to/project --plugin name|git-url|path --variable API_KEY="!@CFATGWE%^WGSFDGSDFW$%^#$%YTHGsdfhsfhyer56734"
A preference tag will need to be present inside the platform tag to make the variable mandatory like so:
<preference name="API_KEY" />
plugman should check that these required preferences are passed in, and if not, should warn the user on how to pass the variable in and exit with a non-zero code.
Certain variable names should be reserved - these are listed below.
$PACKAGE_NAME
The reverse-domain style unique identifier for the package - corresponding to
the CFBundleIdentifier
on iOS or the package
attribute of the top-level
manifest
element in an AndroidManifest.xml
file.
Project Directory Structure
TODO: show how the foo plugin example from above will have its files placed in a cordova project after running plugman
Authors
- Andrew Lunny
- Fil Maj
- Mike Reinstein
- Anis Kadri
- Braden Shepherdson
- Tim Kim
Contributors
License
Apache
TODO
These apply to plugman as well as cordova-cli. Keep the two in step, they both have future
branches.
These are in rough order of priority, most urgent at the top.
- Fix all the tests, including the www-only tests, which expect the old
www
platform that has been removed. Note that most of the tests will need some rewiring because of the separation of --fetch
and --install
. CB-2814. Assigned to Tim. - Implement a
cordova watch
a la grunt watch
that will re-run cordova prepare
every time the installed plugins change (including those installed with --link
). This is definitely a stretch goal, but it would be awesome. Not assigned but tracked at CB-2819.