Jonathan Peppers

Xamarin, C# nerd, at Microsoft

Xamarin Android Builds in Jenkins

Recently at Hitcents we have been working on deploying our Android version of Draw a Stickman: EPIC to app stores across China. China’s Android app market is a strange place, and it has somewhere around 30 app stores. Google Play is not available in China, so many small stores popped up all over the country to fill the gap. Several of the stores are mobile providers, such as China Mobile, and run payments through users’ cell phone plan (which is prepaid a lot of the time).

iOS is not a huge player over there, since iPhones have a luxury tax on them--only the truly rich can afford it. Our iOS version of the app has been released for a while, but not with the success that is possible on Android. You can buy an Android 2.3 device for about $16 US, so the Android market is pretty ripe for the taking. Combine this with ultra-micro IAPs, you can charge as little as 1 CNY (or ⅙ of a USD), and freemium apps become a very attractive way to monetize.

So our huge challenge (apart from integrating IAPs for these app stores) was that we needed to build 20 APKs with various package names, settings, images, jar files, etc. all depending on the store. You can imagine this would be a daunting task, and can feel the pain with a Visual Studio solution that looks like this:

So I quickly came up with the following approach, which is probably the only thing that has kept us sane:

  • I’d write a test of unit tests that have the sole purpose of validating each project
  • Validate the Android Manifest
  • Validate the csproj file for each app store, of required files
  • Setup something to replace the version numbers in the AndroidManifest.xml file to match our source control revision number
  • Setup our CI server, Jenkins, to package up 20 APKs
  • Setup Jenkins to upload the APKs to Dropbox
  • Normally we’d use TestFlight, but the app name being 画个火柴人 for every store, you cannot easily differentiate builds with the same app name on TestFlight

If we hadn’t have done this, I would say our task would have been impossible. Hand creating these projects without validation of needed files would have been a nightmare. Additionally, if we had to make each APK by hand from inside Visual Studio, we would have wasted a lot of time with a tight deadline looming overhead.

So here’s how I accomplished each step:

  • I validated the Android manifest with a list of XPath expressions.
  • I validated the *.csproj file by looping through <ItemGroup> tags and comparing against file lists we created. I also double-checked the build action, and if a file is linked-in or not.
  • I found a way to setup MSBuild to modify the Android version numbers in our manifest file.
  • I found the correct command line arguments to build, package, and sign our APKs.
  • Lastly, I copied the successful builds to a Dropbox folder on our build server, and let the Dropbox application upload the files (not the best solution, but the quickest).

If you are familiar with setting up builds in Jenkins, each step is pretty straightforward. We have had Jenkins running on our build server for a long time, since back when it was called Hudson. Ours is running on Windows, but we have setup Mac slaves in the past to build iOS projects, too.

Here is how my build configuration is setup:

Setup SVN to checkout (we don’t use Git for games, we quickly discovered artists and animators can’t figure out Git):

It’s important to select the “emulate clean checkout” option. I like it because it will delete all bin/obj folders, and if our build mangles an AndroidManifest.xml file, it will get reverted.

So next, build the unit test project, this is a simple example of adding an MSBuild step:

I always check the “Pass build parameters as properties” box, this gives you the ability to access Jenkins variables inside your project files with MSBuild.

Then run the tests, I’m taking advantage of some Jenkins variables I’ve configured:

There isn’t a Jenkins plugin for running MsTest, so I had to resort to a batch command. I could have easily used NUnit here, too. If MsTest.exe has a failing test, it will return a bad exit code and stop the build right here.

Next, we needed to set up our AndroidManifest.xml file to reflect the SVN revision number on our APK file. Luckily there is an MSBuild task called XmlPoke, that allows you to transform XML files with XPath.

Add the following to a csproj file, just before the closing </Project> tag:

This looks pretty messed up (XML inception, at least we’re not escaping our escaped XML), but it works. SVN_REVISION is automatically passed in by Jenkins, and our Jenkins server has a global environment variable for JENKINS=1. This prevents builds we make when developing from modifying the manifest file.

Next, we need to package our Android project in Jenkins:

The important bit is this line of properties:

If you needed to run this outside of Jenkins, open up the Visual Studio cmd prompt and you can just run msbuild YourProject.csproj followed by the above properties.

Last but not least, we have to set up our post-build steps for Jenkins:

The entire build would probably not work without activating Chuck Norris. I highly recommend this for all Jenkins projects.

The MsTest result report will show nice graphs and regression history in Jenkins, so it is just nice to turn on.

Lastly, we used a plugin called ArtifactDeployer to copy our signed APKs to a Dropbox directory on our build machine. We then got lazy and relied on the Dropbox app on our build server to do the work uploading (make sure to turn off upload throttling in settings!).

Overall, I set up this entire process  in two days due to a tight deadline. There is definitely room for improvement since the build completes in Jenkins before the Dropbox upload is complete. Otherwise, I hope this helps someone out there trying to make automated builds work for Xamarin.Android. I have not tested any of this on the Mac with xbuild, but I presume it will work fine if XmlPoke is implemented.

comments powered by Disqus