Speed up your Android Gradle build
Last week at Groupon we held another internal Global hackathon (#GeekonFeb2016). Twice a year, for one week engineering and product teams pause their normal work and spend time on different projects. This time the theme was to “fix something internal”. As I’ve always wanted to speed up our CI android builds, so I decided to spend a few days out of my week fixing this problem.
Before the hackathon began, this process took more than 9 minutes.
The steps executed are the following:
gradle initialization
build
3000+ unit tests with jacoco coverage
static code checks: lint, pmd, findbugs, checkstyle
(build release flavor apks on specific branches and send them to nexus)
You might think that the unit testing takes most of the time but they actually run less than 30 seconds. We were constantly moving from Robolectric to pure Junit tests since Google supported this; these run much faster. The process is ongoing but a 30 seconds span of time is not the big win we are looking for here; it’s the static checks that takes up to 1/3rd of our build time:
Lint, Total time: 43.143 secs
Findbugs: Total time: 2 mins 1.861 secs
Pmd, Total time: 2 mins 11.487 secs
checkstyle, Total time: 24.246 secs
One option would be to move them out of our CI build and let them run asynchronously on a sonar server. But, this was not an option for us due to our zero-tolerance policy for warnings. If only one of the tools could find something in the build that is marked unstable, so the pull-request fails (which is shown by a red light in the office; another small Geekon project this year).
So we need to stick synchronously but speed this up. My believe for this Geekon was that those checks can be parallized. They should not manipulate anything that another task might depend upon. They should just scan over the source or binary files for patterns.
Gradle does only supports parallelization for multi projects builds. Until recently; the latest version now supports an incubation feature for parallelizing tasks.
When writing your tasks you simply add the annotation:
@ParallelizableTask
This tells Gradle that this task can run parallel to others.
To be fair, its a bit more complicated for most tasks; lots of internal rules can override the annotation.
Problem with the annotation is, we don’t own the tasks. But we can cheat here by extending it:
@ParallelizableTask
class ParallelCheckstyle extends Checkstyle {
}
task checkstyle(type: ParallelCheckstyle) {
…
Now we run our tasks by enabling the incubation feature with:
gradlew -Dorg.gradle.parallel.intra=true — parallel lint findbugs checkstyle pmd
I did this for checkstyle, pmd and findbugs and the result was promising, from 9+ minutes we came down to 5min 22sec.
Another 30 seconds could be saved doing the same from Lint, but this will take a bit more time!
PS: if you want your build faster in general, check this post: https://medium.com/the-engineering-team/speeding-up-gradle-builds-619c442113cb