This document can be read in Google Docs (http://docs.google.com/View?id=ddwc44gs_226ds4kfpff), cut and paste link if you have problems accessing it. If you have problems seeing the images, like the icon in this box don't blame me, it seems to be an issue when you publish to Blogspot from Google Docs. |
introduction
"Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible."
We have written some tests for our project and now we would like to take continuous integration into account, mainly if we are working in an enterprise development environment and our team has several developers.
We all are very much accustom to use Eclipse and Android ADT as our main developer environment, so it's logical that even though we are increasing our team size we don't want to left behind that experience and start using a different tool from scratch. At the same time, it's pretty clear that using maven as a build tool for the project we obtain a great level of simplification, mainly if we use hudson, cruise control or similar continuous integration tools.
That's why the approach presented here is to let both models co-exist.
project structure
We are also considering here that a VCS is used and a parent project containing both the main and test project is under this VCS control.
The structure of our project P1 will be
the P1 project including AP1 application's main project and its tests in AP1Test.
To create these projects you should follow this sequence:
- Create a Project (File -> New... -> Project), P1 in this case
- Add a pom.xml file (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
- Create an Android Project (New... -> Android Project), AP1, using P1 folder instead of the default location, but DON'T create the test project yet or it will fail (ADT 0.9.5)
- Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
- Selecting AP1 project, create the corresponding test project AP1Test (Android Tools -> New Test Project) using again P1 as the location
- Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
- You can now import the whole project structure to your VCS
pom files
You need to define some things inside your pom files in order to get the Android projects correctly built.
P1's pom.xml
We are using maven-android-plugin here, visit its site for further details.
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>P1</groupId> 4 <artifactId>P1</artifactId> 5 <packaging>pom</packaging> 6 <version>0.0.1-SNAPSHOT</version> 7 8 <build> 9 <sourceDirectory>src</sourceDirectory> 10 <plugins> 11 <plugin> 12 <groupId>org.apache.maven.plugins</groupId> 13 <artifactId>maven-compiler-plugin</artifactId> 14 <version>2.1</version> 15 <configuration> 16 <source>1.6</source> 17 <target>1.6</target> 18 </configuration> 19 </plugin> 20 <!-- 21 maven-android-plugin doesn't delete files from bin, 22 only from target, we are deleting them here 23 --> 24 <plugin> 25 <groupId>org.apache.maven.plugins</groupId> 26 <artifactId>maven-clean-plugin</artifactId> 27 <version>2.2</version> 28 <configuration> 29 <filesets> 30 <fileset> 31 <directory>bin</directory> 32 <includes> 33 <include>**/*</include> 34 </includes> 35 </fileset> 36 <fileset> 37 <directory>gen</directory> 38 <includes> 39 <include>**/*</include> 40 </includes> 41 </fileset> 42 </filesets> 43 </configuration> 44 </plugin> 45 <plugin> 46 <groupId>com.jayway.maven.plugins.android.generation2</groupId> 47 <artifactId>maven-android-plugin</artifactId> 48 <configuration> 49 <sdk> 50 <!-- <path>${env.ANDROID_HOME}</path> --> 51 <path>/opt/android-sdk/</path> 52 <platform>2.1</platform> 53 </sdk> 54 <deleteConflictingFiles>true</deleteConflictingFiles> 55 </configuration> 56 <extensions>true</extensions> 57 </plugin> 58 </plugins> 59 </build> 60 <modules> 61 <module>AP1</module> 62 <module>AP1Test</module> 63 </modules> 64 <dependencies> 65 <dependency> 66 <groupId>android</groupId> 67 <artifactId>android</artifactId> 68 <version>2.1</version> 69 <type>jar</type> 70 <scope>provided</scope> 71 </dependency> 72 </dependencies> 73 74 <dependencyManagement> 75 <dependencies> 76 <dependency> 77 <groupId>android</groupId> 78 <artifactId>android</artifactId> 79 <version>2.1</version> 80 <scope>provided</scope> 81 </dependency> 82 </dependencies> 83 </dependencyManagement> 84 </project> |
Basically we
- define our source directory as src to be compatible with the Eclipse Android project structure
- configure the maven-compiler-plugin according to our needs
- clean Eclipse Android project structure too in our clean goal
- configure maven-android-plugin defining the location of the Android SDK. This can be defined here or read from the environment
- define our modules, in this case the main project AP1 and its tests AP1Test
- define the dependencies, android 2.1 in this case
AP1's pom.xml
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>AP1</groupId> 4 <artifactId>AP1</artifactId> 5 <packaging>apk</packaging> 6 <version>0.0.1-SNAPSHOT</version> 7 <parent> 8 <artifactId>P1</artifactId> 9 <groupId>P1</groupId> 10 <version>0.0.1-SNAPSHOT</version> 11 </parent> 12 </project> |
AP1Test's pom.xml
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>AP1Test</groupId> 4 <artifactId>AP1Test</artifactId> 5 <packaging>apk</packaging> 6 <version>0.0.1-SNAPSHOT</version> 7 <parent> 8 <artifactId>P1</artifactId> 9 <groupId>P1</groupId> 10 <version>0.0.1-SNAPSHOT</version> 11 </parent> 12 <dependencies> 13 <dependency> 14 <groupId>AP1</groupId> 15 <artifactId>AP1</artifactId> 16 <version>0.0.1-SANPSHOT</version> 17 </dependency> 18 <dependency> 19 <groupId>AP1</groupId> 20 <artifactId>AP1</artifactId> 21 <version>0.0.1-SNAPSHOT</version> 22 <type>apk</type> 23 </dependency> 24 </dependencies> 25 </project> |
populating your local repository
For maven to find android dependencies you must populate your local repository. To ease the task you can use android-mvn-install script which do all the hard work for you.
Just run
$ wget -qO - http://android.codtech.com/android-tools/android-mvn-install | bash -s -- --sdk-dir=/opt/android |
or download from its page and run it locally.
There's an alternative for this step, maven-android-sdk-deployer, however I prefer the simplicity and flexibility of the android-mvn-install script.
building the project
Now we have several alternatives to build our project:
- using Eclipse as usual for other Android projects using ADT plugin (i.e.: AP1 -> Run As -> Android Application)
- using Maven from Eclipse (i.e.: P1 -> Run As -> Maven build)
- using Maven from the command line (i.e.: mvn install)
- using a continuous integration tool
using hudson
Having followed all the steps mentioned before we will now be able to create a job in hudson to build our project using maven
conclusion
There are still some rough edges and some things we haven't mentioned yet like running headless android emulators to be able to run the tests on the server. We will be covering these issues in future posts but I think that we have enough already to start applying continuous integration for our android projects.
Comments are gladly welcome.
Copyright © 2010 Diego Torres Milano. All rights reserved.
17 comments:
Hi Diego,
Have you been able to run the emulator headless ?? I'm currently setting up a scenario very similar to what you are working on, but I would like hudson to run our instrumentation tests. I would appreciate any help on this.
Thanks,
Daniel.
Hi Daniel,
Yes, I'm running some headless emulators on the continuous integration server. Hudson run the tests.
What's the problem ?
Please give me some details so I can help you.
Hey Diego,
I haven't found how to run a headless emulator. Can you show me the command to run to do this ?
Thanks,
Daniel
heh, just found it.
emulator -avd myavd -no-window.
That's right !
Additionally you may want to add some extra parameters to have something like $ emulator -avd 2.1 -no-window -no-audio -qemu -vnc :2 to avoid audio and to have a vnc console however the commands accessible through it might not what you expect.
Hey Diego, thanks for your comments.
Question: How can I add an external jar to my apk package using the maven-android-plugin?
Thanks,
Daniel
I've added some description of how to add external libraries to the online version of the document because some screenshots were also included and will look much better there.
See it at http://docs.google.com/View?id=ddwc44gs_226ds4kfpff
You can also do this using the unpack goal for the maven-dependency-plugin. Here's an example I used for my project:
http://github.com/hugojosefson/maven-android-plugin-assets-dependency/blob/master/assets-dependency-app/pom.xml
Cheers!
Hi Guys,
If you're interested, I've been working on a plug-in for better Eclipse-ADT-Maven integration which also resolves the issue of dependency management:
http://code.google.com/p/m2eclipse-android-integration/
Let me know what you think.
-- Ricardo
Hi Ricardo,
Thanks for the info. I will be testing the plugin soon.
Hi Diego,
we have a similar setup (Android/Maven/Hudson), but whenever we try to send key events to a headless emulator in an instrumentation test, the test will fail.
The logs say it's a permission problem with key event injection. The build works when run on an emulator that does have a window, so it must be related to the -no-window flag.
Does InstrumentationTestCase.sendKeys() work for you on a headless emulator?
Do your tests send the events normally if the emulator is not headless ?
sorry for not following up quickly enough -- actually we found out that it's not the -no-window flag, it must be something related to the emulator itself.
After hours of fumbling around, we found that whenever the build server starts a new emulator instance (using android:emulator-start), the build will fail because of the broken event injection permissions. However, if an emulator is already running, the build succeeds.
Still, on our dev machines, the build ALWAYS succeeds, regardless whether an emulator has to be started first or not.
Do you know what could actually be causing these permission problems? I have posted a more detailed description of the errors that pop up in the device logs here:
http://stackoverflow.com/questions/3214531/how-to-send-key-events-to-a-headless-emulator-in-an-instrumentation-test
Have you run code coverage using hudson and emma plugin using the set up written in this post (source code in APP and android JUNIT test in APPTest ). If you can share the pom file it will be great. Thanks!
Hi!
I have an android library project, and its related instrumentation test project. Both projects are built using maven, and under CI within Hudson. I'm looking for a way to include emma coverage report to my CI job, but didn't find the way to generate emma coverage report from maven build yet...
Do you have any advice?
Thanks,
Lorie.
Thanks for the afflatus I was fatigued by appointment but i learn that action is about alive to the fullest and able every moment.
Android developer
Post a Comment