
Automate Your Mobile Development Builds With Antenna
A month ago I started at a new position at a company doing some really interesting development for mobile phones using Java. I have been wanting to get into mobile development for a while now and have done some development for the iPhone, but nothing serious so, when this opportunity presented itself, I jumped at the chance.
The project I am currently working on is based on a proof of concept and as such is still in the very early stages. One of the first things I wanted to do was to find a way to automate the build and deployment process as much as possible. Having been a big fan of Ant this was where I turned first. Ant itself has many tasks that can automate a lot of the mundane tasks associated with your daily development cycle but I was looking for some tools to handle the more specific tasks of creating COD files, signing if need be and deploying the resulting files for over-the-air (OTA) installation to the BlackBerry device.
After some searching I stumbled upon Antenna, an extension to the core Ant tasks focused specifically on mobile development, exactly what I need. And with that, I set forth to automate my build process using Antenna. Following then is my experience getting an Antenna build set-up and running. In this article I am targeting the BlackBerry platform but, the core of the tasks described here can be applied to any target platform.
Our first stop is to download Ant and then the Antenna jar file. After installing Ant and downloading the Antenna .jar file, you need to copy the Antenna jar to Ant’s lib directory. Next we can start writing our Ant build script. Start off by creating a new file called build.xml with the following skeleton code:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Forms" default="rapc" basedir=".">
<description>Forms Mobile Client</description>
</project>
We will also need to define some properties to use within our build.xml so next create a blank file and call it project.properties. Inside the build.xml just after the ‘description’ tag add the following line to include our properties file:
<property file="project.properties" />
For some of the tasks that we will be using we need to provide some classpath variables so that the build can complete successfully. Instead of having to repeat ourself over and over again we can simply set-up out path once and reference in the relevant tasks. Add the following code:
<!-- Classpath settings -->
<path id="project.class.path">
<pathelement path="${java.lib}"/>
<pathelement path="${bb.api.jar}" />
<pathelement path="${path.antenna.jar}" />
</path>
Here we then also encounter the first instance where we will be using some of the property elements defined in our properties file. Open the properties file and add the following to it:
java.lib=C\:\\Program Files\\Java\\jdk1.6.0_07\\lib bb.api.jar=C\:\\Program Files\\Research In Motion\\BlackBerry JDE 4.7.0\\lib\\net_rim_api.jar path.antenna.jar=C\:\\ant\\lib\\antenna-bin-1.1.0-beta.jar
You should change the location where the above point to the location they are located on your file system. Also note the escape sequence needed on Windows. An important variable we need to set in our properties file is the root location of our mobile SDK. Antenna looks for a variable named wtk.home for this so, add the following line to your properties file:
wtk.home=C\:\\Java_ME_platform_SDK_3.0
It is time to write our first target. The purpose of this target is to set-up our environment by loading the Antenna tasks and setting up our class path. dd the following to your build.xml:
<!-- Initialize the Antenna environment -->
<target name="initialize">
<echo message="Initializing Antenna build environment..."/>
<taskdef resource="antenna.properties">
<classpath refid="project.class.path" />
</taskdef>
</target>
Inside out target, we first simply echo a message to the console about what our script is doing and then load our task definition from the antenna.properties file. By pointing our task definition to the resource as above it will load the antenna.properties file that is included inside the antenna.jar. This properties file contains a list of all of the tasks that Antenna exposes and the location within the code where these tasks are defined. If you unzipped the jar file, you will find this file, below is a listing of it’s contents:
wtkjad=de.pleumann.antenna.WtkJad wtkbuild=de.pleumann.antenna.WtkBuild wtkpackage=de.pleumann.antenna.WtkPackage wtkmakeprc=de.pleumann.antenna.WtkMakePrc wtkrun=de.pleumann.antenna.WtkRun wtkpreverify=de.pleumann.antenna.WtkPreverify wtkobfuscate=de.pleumann.antenna.WtkObfuscate wtksmartlink=de.pleumann.antenna.WtkSmartLink wtkpreprocess=de.pleumann.antenna.WtkPreprocess wtkdeploy=de.pleumann.antenna.WtkDeploy wtkrapc=de.pleumann.antenna.WtkRapc wtksign=de.pleumann.antenna.WtkSign wtkjadattrib=de.pleumann.antenna.JadAttributes wtkdevice=de.pleumann.antenna.device.Device
Inside our task definition we first make use of the class path we defined earlier. Instead off repeating all of the lines here, we simply pass a reference to our earlier definition using the id ‘project.class.path’. With our build environment set-up we are ready to write the task that will compile our source files:
<!-- This will compile all src files into the build directory. -->
<target name="compile" description="Compile Java sources" depends="initialize">
<mkdir dir="${build.root.classes.dir}"/>
<wtkbuild bootclasspath="${boot.classpath}" destdir="${build.root.classes.dir}" encoding="${src.encoding}" srcdir="${src.root.dir}" preverify="false">
<classpath refid="project.class.path" />
</wtkbuild>
</target>
At the root of your project directory you should have a directory called ‘src’ that holds all of your Java source files. The first task in our target is to create the directory to which we will compile our source files. In your properties file add the following:
build.root=build
build.root.classes.dir=${build.root}/classes
Here we can also see how we can reuse other variables defined in our properties file. With our destination directory created we can invoke the ‘wtkbuild’ task to build our source files. Antenna’s wtkbuild task provides the exact same functionality as the Ant javac task but provides some extra attributes that can be set as well as setting certain attributes such as target and bootclasspath to useful default values.
Although I mention above that bootclasspath is one of the attributes that is set to a default value, you will find that in the code above I do set it myself. The reason for this is because of my very specific target platform. The default that Antenna will set bootclasspath to is the file midpapi.zip in your tool kit. However, I am targeting the BlackBerry platform and therefore point the bootclasspath to the BlackBerry API by setting the variable boot.classpath as follows:
bb.api.jar=C\:\\Program Files\\Research In Motion\\BlackBerry JDE 4.7.0\\lib\\net_rim_api.jar
boot.classpath=${bb.api.jar}
I also pass false to the preverify attribute as I will handle this step a little later when packaging the application. The reason we need to preverify our compiled class files is because it adds certain annotations to the resulting bytecode and allows the VM on the mobile device to skip it’s own preverify checks and therefore run your application a lot quicker. You can read more about preverifying on jGuru. As a final step we also pass in the classpath by referencing the one previously set.
With our code compiled we need to get our directory structure ready so that we can generate our final files and package our application, ready for deployment. For this I use a simple target to prepare our final directory:
<!-- Collects all required application artifacts before packaging as .jar -->
<target name="prepare">
<mkdir dir="${build.root.deploy.dir}" />
<mkdir dir="${build.root.prepare.dir}"/>
<copy todir="${build.root.prepare.dir}">
<fileset dir="${build.root.classes.dir}" />
</copy>
<mkdir dir="${build.root.prepare.dir}/resources"/>
<copy todir="${build.root.prepare.dir}/resources">
<fileset dir="${resources.dir}" />
</copy>
</target>
To prepare our directory I use a couple of simple mkdir and copy Ant tasks to move the needed files over to their respective locations. The code above is self explanatory so we will move on and add the following to our properties file:
src.root.dir=src
resources.dir=${src.root.dir}/resources
build.root.classes.dir=${build.root}/classes
build.root.prepare.dir=${build.root}/prepare
build.root.deploy.dir=${build.root}/deploy
You can of course extend or simplify the above target to meet your specific needs. Having compiled our sources, moved all of the required files to a central location we can move on to the next step and create our JAD(Java Application Descriptor) file.
<!-- Builds the JAD file -->
<target name="jad">
<wtkjad jadfile="${jadfile}"
name="Forms Client"
vendor="OpenSource Release Feed"
version="1.0">
<midlet name="${codename}" icon="${midlet.icon}" class="${midlet.class}" />
</wtkjad>
</target>
The JAD file that will be generated by the above task is nothing more then a simple text file that describes our application. In the wtkjad target we first specify the location where we want our JAD file to be generated. We then continue to specify some attributes to be written to the JAD file such as the name of the application, the vendor that developed it, version number etc.
Inside your wtkjad target you can have a nested midlet and attribute declaration to provide some extra information the the JAD task it will use when generating the file. In out case we are specifying a midlet and providing it’s name, the location of the icon that should be used to represent the application on the device as well as the midlet class. To enable us to run the task we need the following entries in our properties file:
jadfile=${build.root.deploy.dir}/Forms_Client.jad
codename=Forms_Client
midlet.icon=${resources.dir}/icon.png
midlet.class=com.opensourcereleasefeed.Forms_Client
Once this task has completed, you can open the JAD file from the location you specified above. The current JAD file should contain something similar to the following:
Manifest-Version: 1.0 MIDlet-Version: 1.1 Created-By: 10.0-b23 (Sun Microsystems Inc.) MicroEdition-Configuration: CLDC-1.0 MIDlet-1: DCT_Client, src/resources/icon.png, com.opensourcereleasefeed.Forms_Client MIDlet-Vendor: OpenSource Release Feed
With all of this done we are ready to package everything up in a jar file:
<!-- Builds the Obfuscated And Preverified JAR file -->
<target name="package" depends="compile, prepare, jad">
<wtkpackage bootclasspathref="project.class.path" autoversion="${flag.autoversion}" jadfile="${jadfile}" jarfile="${jarfile}" obfuscate="true" preverify="true" nofloat="false" nofinalize="false" cldc="false">
<fileset dir="${build.root.prepare.dir}" />
<classpath refid="project.class.path" />
</wtkpackage>
</target>
There is quite a lot going on here so let’s take this line by line. On the very first line you will see a new attribute added to the target tag called ‘depends’, not to be confused with the adult diapers, this attribute tells Ant that before it can run this task, the tasks compile, prepare and jad must have completed.
As with wtkbuild the wtkpackage task is an extension of the default Ant jar task. Again here we need to specify the boot class path but only because this application is dependent on libraries that does not form part of the default MIDP classes. As a side note, the libraries specified in the boot class path is used during obfuscation and/or preverification but is not included in the final JAR file. If you need to specify libraries your code depends on and that should also be included in the resulting JAR file you should use the libclasspath attribute.
Autoversion is an optional attribute flag that can be set to tell the task to automatically increase the MIDLet version key in the JAD file. The jadfile attribute here needs to point to an existing JAD file as this task will not create the JAD file for you. The jarfile attribute specified where the JAR file should be written to as well as the name of the JAR file.
Next follows a couple of flags that you can set but it is definitely suggested to set both obfuscate and preverify to true at this point as your final deployed application will definitely greatly benefit from both off these operations. During preverification the process will check for certain coding aspects that in general is not a good idea when developing for mobile devices. In particular it checks for the use of floats as well as the use of finalize. There is also the option to set the nonative flag here but, if you are going to pass false to either nonative or nofinalize you also need to set cldc to false or else the other two flags will have no effect. Also note that if you set obfuscate to true, you have to set preverify to true as well or you might very well run into the missing stack map at… error.
Inside the wtkpackage task we add a fileset that points the task to the files that should be included in the final jar and we again pass in our classpath settings. There is quite a bit to this task so to read more please see the documentation on the Antenna website. To complete this part add the following to your properties file.
flag.autoversion=true
jarfile=${build.root.deploy.dir}/Forms.jar
With our Jar created, obfuscated and verified we can move to creating our COD file. The COD file is a specific deployment type for the BlackBerry device, you find a little more information on it in this post:
<!-- Generate the COD file -->
<target name="rapc" description="RIM COD Compiler" depends="package">
<wtkrapc jadfile="${jadfile}" source="${jarfile}" codename="${codename}" import="${bb.api.jar}" destdir="${build.root}" quiet="true" midlet="true" />
<antcall target="expand" />
</target>
The wtkrapc task is BlackBerry specific and is used to generate our COD file that we will need once we want to deploy our application to the actual device. Our first two attributes point the rapc executable to our, already generated, JAD and JAR files. The codename attribute specifies the name of the generated COD file.
You will notice that the bootclasspath attribute is missing in this task but instead you find the import attribute. You can pass exactly the same to this attribute as you would have to the bootclasspath attribute. The final couple of attributes simply tells rapc where to generate the COD file, which mode to use for output to the console and whether our application is built as a MIDlet.
Technically this should be it, you simply run the rapc target and as it depends on package which depends on all of our other tasks, everything will be compiled, jarred up, obfuscated, preverified and our final COD file generated for deployment to our device. Unfortunately this is not always the case as the BlackBerry seems to have a problem with it’s own COD file format. As you can see from the forum, the best way to overcome this issue is to extract the COD file before deploying it to the device. Looking back at the rapc target above you will see that I include an antcall, pointed towards the expand target:
<!-- Unzips the COD file -->
<target name="expand">
<unzip src="${codfile}" dest="${build.root.deploy.dir}" />
</target>
The above simply uses Ant’s own unzip task to extract the COD file into the final deployment directory where our JAD and JAR files are already located. That is it! You are now all set to deploy you final BlackBerry application using over-th-air provisioning. How you go about this is basically up to you and depends on your environment and set-up. Tomorrow I will cover a couple of ways you can deploy your application for over the air provisioning using Ant and Antenna’s own deploy task respectively.
As you can see from this Antenna is an incredibly useful tool to have in your toolbox when developing mobile applications and I will definitely encourage everyone to give it a go. There is even more to Antenna then is covered here so take some time and read the documentation over on the Antenna website and as always I look forward to read your comments, tips and tricks with regards to Antenna and automating your mobile builds in general.
November 24, 2009
Excellent information on Antenna!. Waiting on your OTA block
My recent post Moblogging Test
November 29, 2009
Glad you enjoyed it! Just posted the follow up article: http://expansive-derivation.ossreleasefeed.com/20...