Chris Price, June 29th, 2012
This post introduces a Maven plugin for easily building native PhoneGap apps for iOS, Android, WP7 (and more) from any WAR project, in any environment. It assumes that you are familiar with using Maven and have a WAR project you wish to build. In return, when you run your build you’ll get native binaries automatically built then installed/deployed to your repository. Best of all because it’s all part of your standard Maven build, your CI server can handle your one-click release builds signed with your distribution keys ready for app store distribution.
Introduction
PhoneGap Build is a hosted service which wraps HTML5 applications into native applications. The plugin connects to the service, uploads the exploded WAR directory, waits for the builds to complete and then downloads the native binaries installing/deploying them as attached artifacts.
Building your first native app
First of all you’re going to need to visit the PhoneGap Build website and sign up for an Adobe account (GitHub doesn’t work with the API unfortunately). Test out your shiny new credentials by running the following command from a terminal -
mvn com.github.chrisprice:phonegap-build-maven-plugin:list -Dphonegap-build.username=[USERNAME] -Dphonegap-build.password=[PASSWORD] |
If everything works as planned then you should see all your applications and keys that are currently hosted on the service (empty that is, if you just signed up!). If there’s a problem then double check the command and also check that your credentials work on the website.
Including the plugin in your project
Now for something more exciting! You’ll need to make two additions to your project, firstly to configure maven to use the plugin during the build lifecycle, and secondly to customise how PhoneGap Build should wrap your application. Start by adding the following to the project>build>plugins section of your POM -
<plugin> <groupId>com.github.chrisprice</groupId> <artifactId>phonegap-build-maven-plugin</artifactId> <version>0.0.2</version> <executions> <execution> <id>phonegap-build</id> <!-- the goals are lifecycle bound by default --> <goals> <goal>clean</goal> <goal>build</goal> </goals> <configuration> <platforms> <platform>android</platform> </platforms> </configuration> </execution> </executions> </plugin> |
Now create a file called config.xml in src/main/phonegap-build which contains a few key details about your application. The config.xml file also gives you fine control over the configuration of the PhoneGap wrappers, documented in detail here however, for now just copy the following -
<?xml version="1.0" encoding="UTF-8" ?> <widget xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0" id="[GROUP-ID].[ARTIFACT-ID]" version="[VERSION]"> <name>PhoneGap Build Sample</name> <description> A sample for phonegap-build-maven-plugin. </description> <author href="http://www.scottlogic.co.uk/blog/chris" email="cprice@scottlogic.co.uk"> Chris Price </author> </widget> |
At the minimum you’ll need to change GROUP-ID, ARTIFACT-ID and VERSION to correspond to the maven project co-ordinates, feel free to change the author etc. but it isn’t required at this point.
Running the build
When you’ve made the changes to the config.xml, you should be able to run the following -
mvn clean install -Dphonegap-build.username=[USERNAME] -Dphonegap-build.password=[PASSWORD] |
You should see your application being packaged locally and then uploaded to the service. After a short wait, the service needs some time to perform the actual build, the native Android apk file will be downloaded and installed in your local repository. Simples!
Multiple platforms
You now have an Android binary without having to mess around with the SDK or any PhoneGap project templates, impressive, but what about if we now get a requirement for a WP7 binary?
If we tweak the plugin configuration in your POM like so -
<platforms> <platform>android</platform> <platform>winphone</platform> </platforms> |
And re-run the command above -
mvn clean install -Dphonegap-build.username=[USERNAME] -Dphonegap-build.password=[PASSWORD] |
You’ll see not only an Android version downloaded and installed but sat alongside it is now a WP7 xap. Building for webOS (webos), BlackBerry (blackberry) and Symbian (symbian) follows exactly the same pattern. iOS (ios) requires a little more configuration, you need to supply your developer certificate and provisioning profile, but once configured a single Maven build can produce native binaries for all the major mobile platforms and install/deploy them straight into your repository.
Configuring the plugin to build for a platform will cause the build to fail if there is an error building for that platform so be sure to only include the ones you are interested in e.g. don’t attempt to build for iOS without configuring your developer certificate and provisioning profile.
Storing your credentials somewhere more secure
One of the things you’ll likely want to change if you start using the plugin in your project is how the service credentials are passed to the plugin. Passing them in on the command line is laborious and worse still very insecure. The best way to do this is to store them as a server in your settings.xml. This has the major advantage of allowing different developers to use their own credentials whilst still sharing the same project. It also allows the passwords to be encrypted if that floats your boat.
Signing your Android build for distribution
When it comes to distributing your application to the Play Store, the developer certificate that by default the service signs Android binaries with just isn’t going to cut it. You’ll need to follow a few steps to generate a self-signed (i.e. free!) private signing key and then configure the plugin to use it. This process is detailed on the plugin website.
Conclusion
This part covered configuring the plugin to build PhoneGap Android applications for development, and also signed distribution builds ready for the app store. In the next part I’ll cover building applications for iOS (which is slightly trickier because you’ll need to configure your developer certificate and provisioning profile).
Until then if you get stuck, please check-out the sample application or post an issue on the bug tracker, if you want to customise the Android PhoneGap wrapper be sure to revisit the config.xml settings and if you want to skip ahead, there’s also more detailed documentation on the plugin site, including instructions for building for iOS.
This entry was posted on Friday, June 29th, 2012 and is filed under Blog.
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site .


I tried your plugin and I was really amazed
In few minutes I got my APK running on my Android tablet!
Great work!
I am now trying to integrate zxing barcode plugin and I am wondering how to do it using your great plugin. Any suggestion?
Good to hear it being used by other folk
I’ve not used any phonegap plugins myself but I don’t think the maven plugin will stand in your way. These links might be useful -
https://github.com/phonegap/phonegap-plugins/tree/master/Android/BarcodeScanner
https://build.phonegap.com/docs/plugins
Ah yes! This is really good for me too. I’ve been using Android for native app development but I’m new to the whole PhoneGap scene.
Thanks!
Great piece of work! One issue I’m having though is trying to use this plugin from both a build server and a developer workstation to build the same app. The phonegap-build server somehow tags requests coming from each environment as a separate app even though the credentials etc. are all identical. For a free acct, this results in the following error:
build failed: Private app limit reached
Have you come across this before?
Found the issue – I needed to add
<appId>xx</appId>
Under the section of the plugin and enter the appId for the app I was trying to build (availale on the phonegap-build website and inside a text file along with the build artifacts from the initial successful build). I guess it defaults to using the creation part of the API if this value is missing.
Yea that’s exactly right, glad you managed to sort it out.
Hi Chris
I’m trying to configure your maven plugin and I’ve followed the documentation but I am getting this error
[INFO] [phonegap-build:clean {execution: phonegap-build}]
10-Jan-2013 16:31:20 org.apache.commons.httpclient.auth.AuthChallengeProcessor selectAuthScheme
INFO: basic authentication scheme selected
10-Jan-2013 16:31:20 org.apache.commons.httpclient.HttpMethodDirector processWWWAuthChallenge
INFO: Failure authenticating with BASIC ‘Application’@build.phonegap.com:443
[INFO] ————————————————————————
[ERROR] FATAL ERROR
[INFO] ————————————————————————
[INFO] Client response status: 401
[INFO] ————————————————————————
[INFO] Trace
com.sun.jersey.api.client.UniformInterfaceException: Client response status: 401
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:686)
at com.sun.jersey.api.client.WebResource.post(WebResource.java:241)
at com.github.chrisprice.phonegapbuild.api.managers.MeManagerImpl.requestToken(MeManagerImpl.java:55)
at com.github.chrisprice.phonegapbuild.api.managers.MeManagerImpl.createRootWebResource(MeManagerImpl.java:49)
at com.github.chrisprice.phonegapbuild.api.managers.MeManagerImpl.createRootWebResource(MeManagerImpl.java:25)
at com.github.chrisprice.phonegapbuild.plugin.AbstractPhoneGapBuildMojo.getRootWebResource(AbstractPhoneGapBuildMojo.java:112)
……
The authentication details I’ve entered in settings.xml are my Adobe ID details.
A quick way of testing credentials is to run the following from within your project folder -
mvn phonegap-build:listThat will attempt to login to the service and list all your projects/keys. If that fails then double check that you can sign into the service via the website, I’ve had problems before where there’s been an updated license agreement that I’ve had to click through or somesuch. Let me know if that’s still failing.
Cheers,
Chris
Thanks for the quick reply, it’s been a bad day today.
I couldn’t even login via the website so had to reset my password. I’m now getting:
[WARNING] iOsServer not specified, falling back to iOsCertificate/iOsCertificatePassword.
[INFO] ————————————————————————
[ERROR] BUILD FAILURE
[INFO] ————————————————————————
[INFO] ios certificate does not exist C:\development\personal-projects\my-app\target\phonegap-build\ios.p12.
I’m buying a licence now so hopefully this will be cleared up. Thank you
Hi Chris
I was thinking this morning, if I use the Phonegap build plugin which requires the index.html to be in src/main/webapp/, does this mean I won’t be able to use the various emulators which I think require the index.html to be in assets/www/index.html.
Do you use your plugin exclusively or are you able to test locally on emulators as well as use phonegap build using the same project structure?
I’ve not tried to use any of the emulators, I do pretty much all my testing in the browser. I’d be suprised if you couldn’t configure them appropriately but if that doesn’t work the plugin uses the following configuration property for the location of index.html -
https://github.com/chrisprice/phonegap-build/blob/master/plugin/src/main/java/com/github/chrisprice/phonegapbuild/plugin/BuildMojo.java#L181
Hi, I’m still getting the following error even though I have put the .p12 and .mobileprovision files into Phonegap build. Do I need to put these files into my project?
[INFO] Building zip: C:\development\personal-projects\my-app\target\phonegap-build\file.zip
17-Jan-2013 12:19:09 org.apache.commons.httpclient.auth.AuthChallengeProcessor selectAuthScheme
INFO: basic authentication scheme selected
[WARNING] iOsServer not specified, falling back to iOsCertificate/iOsCertificatePassword.
[INFO] ————————————————————————
[ERROR] BUILD FAILURE
[INFO] ————————————————————————
[INFO] ios certificate does not exist C:\development\personal-projects\my-app\target\phonegap-build\ios.p12.
Have you followed the instructions for signing for iOS?
http://chrisprice.github.com/phonegap-build/phonegap-build-maven-plugin/signing-for-ios.html
Thanks for the plugin it looks awesome.
I’m having the same problem – I configured the platform to be only android and I still get this error. It looks like it doesn’t read the configuration – I also tried to change the location of config.xml and the plugin couldn’t find it. here is my pom.xml:
../pom.xml
4.0.0
phonegap-client
war
0.0.5
phonegap-client
${basedir}/target/${name}-${version}
com.github.chrisprice
phonegap-build-maven-plugin
0.0.4
phonegap-build
clean
build
phonegap-build
android
Hi Benny,
Sorry the blog comment system has mangled the XML, would you be able to post an issue on GitHub instead?
https://github.com/chrisprice/phonegap-build/issues?state=open
Cheers,
Chris
Yeah, I spotted the document after I posted the comment. I now get:
[INFO] org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “locked” (Class com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse), not marked as ignorable
at [Source: com.sun.jersey.client.apache.ApacheHttpClientHandler$HttpClientResponseInputStream@5d283826; line: 1, column: 64] (through reference chain: com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse["locked"])
[INFO] ————————————————————————
[INFO] Trace
com.sun.jersey.api.client.ClientHandlerException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “locked” (Class com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse), not marked as ignorable
at [Source: com.sun.jersey.client.apache.ApacheHttpClientHandler$HttpClientResponseInputStream@5d283826; line: 1, column: 64] (through reference chain: com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse["locked"])
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:564)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:684)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:568)
at com.github.chrisprice.phonegapbuild.api.managers.KeysManagerImpl.postNewKey(KeysManagerImpl.java:44)
at com.github.chrisprice.phonegapbuild.plugin.utils.IOsKeyManagerImpl.ensureIOsKey(IOsKeyManagerImpl.java:136)
at com.github.chrisprice.phonegapbuild.plugin.BuildMojo.ensureIOsKey(BuildMojo.java:323)
at com.github.chrisprice.phonegapbuild.plugin.BuildMojo.execute(BuildMojo.java:265)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “locked” (Class com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse), not marked as ignorable
at [Source: com.sun.jersey.client.apache.ApacheHttpClientHandler$HttpClientResponseInputStream@5d283826; line: 1, column: 64] (through reference chain: com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse["locked"])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:649)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:635)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1355)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:717)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2695)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1308)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
at com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy.readFrom(JacksonProviderProxy.java:139)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:554)
… 27 more
After trying again, I get:
[INFO] org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “role” (Class com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse), not marked as ignorable
at [Source: com.sun.jersey.client.apache.ApacheHttpClientHandler$HttpClientResponseInputStream@135b4b49; line: 1, column: 133] (through reference chain: com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse["role"])
[INFO] ————————————————————————
[INFO] Trace
com.sun.jersey.api.client.ClientHandlerException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “role” (Class com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse), not marked as ignorable
at [Source: com.sun.jersey.client.apache.ApacheHttpClientHandler$HttpClientResponseInputStream@135b4b49; line: 1, column: 133] (through reference chain: com.github.chrisprice.phonegapbuild.api.data.keys.IOsKeyResponse["role"])
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:564)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
Ah, the problem seems to be the new security model for certificate passwords has added a few more fields to the API response https://build.phonegap.com/blog/upcoming-security-changes see the “Creating a signing key” section.
I’ve created an issue for this (https://github.com/chrisprice/phonegap-build/issues/9) and I’ll hopefully get a chance to fix it over the next couple of days.
For now you may be able to work around it by uploading a key manually, specifying the key id in the pom and manually unlocking the key through the web interface before trying to build, but you might also run into the same problem when the plugin tries to validate the specified key exists…
Thanks, I’ll keep an eye our for the update.
[...] Following steps from this tutorial: http://www.scottlogic.co.uk/2012/06/using-phonegap-build-with-maven/ [...]
Hi Chris, great plugin!
I am running a mvn clean install and I get the following error message: Failed to execute goal com.github.chrisprice:phonegap-build-maven-plugin
:0.0.4:build (phonegap-build) on project globant-rooms-client: Execution phonegap-build of goal com.github.chrisprice:phonegap-build-maven-plugin:0.0.4:build failed: Build error : No index.html found -> [Help 1]
I have the index.html file in the src/main/webapp folder. Also, I’ve added this to the config:
workingDirectory: ${project.build.directory}/phonegap-build
warDirectory: same as working (otherwise it fails)
Could you point me in any direction to solve this please? Thanks!!
Hi Matias,
Please could you post any issues on the bug tracker https://github.com/chrisprice/phonegap-build/issues.
Cheers,
Chris