Oh Goody—My New Build System Gives Me Stack Traces

My pet peeve from Java EE is the “stack trace from hell”, when the app server shares its pain with me, showing a stack trace, instead of telling me in which source file I messed up some setting. Oh goody—I just switched my Android build to Gradle, and it acts the same way. Hello, dev tooks makers. If you can't trace back your failure to the file name/line number of my artifact, you have failed.

Each year, the students from the mobile development class at San Jose State update an app for the Cinequest film festival. Except, this fall, the class isn't offered, so my colleague Chris Pollett and myself do the work pro bono. I figured it's a good chance for me to catch up with Android development and learn how to use Android Studio, the IntelliJ-based IDE that replaces the Android Eclipse plugin.

Not only that, the Ant build system is being replaced by Gradle. Now I can see that people dislike Ant. It's verbose, and you need to add plugins to get any work done. But at least, when stuff went wrong, you had a fighting chance of finding out the problem.

With Gradle, I am supposed to produce a build file like

buildscript {
    repositories {

    dependencies {
        classpath 'com.android.tools.build:gradle:1.3.1'

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.1.0"

No tasks, just a simple and elegant configuration language. This doc gives you an idea just how simple.

As a user of SBT, I know all about simple and elegant build configuration languages. I suppose one day I could try to learn what SBT actually does under the hood. Except, it will take longer than a day. For now, I just copy/paste snippets from StackOverflow and hope for the best.

Hey, if it works for SBT, it'll work for Gradle, I thought. Except that the Cinequest project structure isn't exactly like the standard Android project. The model classes are in a separate library so they can be tested with JUnit outside Android.

So, I reinstalled the Eclipse plugin and used its Gradle export feature. That gave me some build files to start with. Did they work? Of course not. Gradle gave me this:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':Android:preBuild'.
> failed to find Build Tools revision 23.1.0

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

Hello, how about telling me where you found that build number instead of inviting me to scrutinize the stack trace? (I tried --info and it wasn't any better.) After much messing, I managed to fix up all those fiddly numbers in all those fiddly configuration files. There is build.gradle (multiple times) and settings.gradle and, from the olden days, AndroidManifest.xml, and local.properties.

Finally, stuff started happening. And then...I got a Stack Trace from Hell.

Oh yes, the good old com.android.dex.DexException. Well known to stackoverflow.com (which maybe should change its name to stacktrace.com).

In my case, something changed in the rules, and you can no longer have a library with the same package as the app, or some such thing. The offending artifact was one of the AndroidManifest.xml files. Now really. Is it so hard to say “I've picked up these package names from lines x and y of these files”?

My feeling is that, if you hide a whole complex workflow behind an elegant DSL, that's great, but you owe it to the users to trace errors back to file names and line numbers. It's not enough for a build system (or an application server, or any other kind of developer tool) to work brilliantly in the happy day scenario. If I waste serious time when stuff goes wrong, as it always does, then productivity is out of the window. In the end, productivity is more valuable than elegance.