Sunday, January 17, 2010

Android Continuos Integration: Build with maven

Android Continuous Integration: Build with maven









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:






    1. Create a Project (File -> New... -> Project), P1 in this case
    2. Add a pom.xml file (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    3. 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)
    4. Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    5. Selecting AP1 project, create the corresponding test project AP1Test (Android Tools -> New Test Project) using again P1 as the location
    6. Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    7. 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
    1. define our source directory as src to be compatible with the Eclipse Android project structure
    2. configure the maven-compiler-plugin according to our needs
    3. clean Eclipse Android project structure too in our clean goal
    4. configure maven-android-plugin defining the location of the Android SDK. This can be defined here or read from the environment
    5. define our modules, in this case the main project AP1 and its tests AP1Test
    6. 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:

    Daniel said...

    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.

    Diego Torres Milano said...

    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.

    Daniel said...

    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

    Daniel said...

    heh, just found it.
    emulator -avd myavd -no-window.

    Diego Torres Milano said...

    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.

    Daniel said...

    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

    Diego Torres Milano said...

    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

    Daniel said...

    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!

    Ricardo said...

    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

    Diego Torres Milano said...

    Hi Ricardo,
    Thanks for the info. I will be testing the plugin soon.

    Matthias said...

    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?

    Diego Torres Milano said...

    Do your tests send the events normally if the emulator is not headless ?

    Matthias said...

    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

    Unknown said...
    This comment has been removed by the author.
    shwety said...

    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!

    Lorie said...

    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.

    Android app developers said...

    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