Speed Up Your Gradle Builds with JFrog Artifactory

Speed up your Gradle builds

Gradle introduced a cool built-in feature that lets you cache task outputs. Why is this cool? Because it reduces build time. How? By sharing the output of Gradle tasks between machines, subsequent builds are accelerated since they can reuse those outputs instead of rebuilding them.
Now, this is where it gets even cooler. This feature supports not only your local filesystem cache, but also remote caches that can be shared across your organization.

Let’s start by trying this out with an example using JFrog Artifactory acting as the Gradle build cache. This example will show how we get the build time down from 11 seconds to 1 second !!!

A typical scenario

We’ll start with a simple use case, where the CI server builds a project and stores the build cache in Artifactory for later use by the following builds. This will greatly improve the build time in your local developer environments.

Gradle Build Cache

JFrog Artifactory part

We’ll start by simply creating a generic repository in Artifactory. In our example we’ll call it “gradle-cache-example”, and yes that’s it!

Gradle part

We’ll configure Gradle to use the build cache and point it to Artifactory using the following configurations:

gradle.properties

artifactory_user=admin
artifactory_password=password
artifactory_url=https://localhost:8081/artifactory
org.gradle.caching=true
gradle.cache.push=false

settings.gradle

include "shared", "api", "services:webservice"

ext.isPush = getProperty('gradle.cache.push')

buildCache {
  local {
    enabled = false
  }
  remote(HttpBuildCache) {
   url = "${artifactory_url}/gradle-cache-example/"
   credentials {
    username = "${artifactory_user}"
    password = "${artifactory_password}"
   }
   push = isPush
  }
}

We’ll set the gradle.cache.push property to true, on the CI server, by overriding it using -Pgradle.cache.push=true.

Now, we can build this project simulating the CI server:

15:13 $ ./gradlew clean build -Pgradle.cache.push=true
Configuration named 'published' does not exist for project ':services' in task ':services:artifactoryPublish'.
Cannot publish pom for project ':services' since it does not contain the Maven plugin install task and task ':services:artifactoryPublish' does not specify a custom pom path.
Build cache is an incubating feature.
Using remote HTTP build cache for the root build (authenticated = true, url = https://localhost:8081/artifactory/gradle-cache-example/).
BUILD SUCCESSFUL in 11s
13 actionable tasks: 12 executed, 1 up-to-date

The build took 11s. This sounds about right since we have a test that just sleeps for 10 seconds:

public void testClasspath() throws InterruptedException {
  System.out.println("Executing heavy fake test");
  Thread.sleep(10000);
  new TestTest().method();
}

Now, let’s run the build a second time. Remember that the cache is now populated by the CI.

15:29 $ ./gradlew clean build
Configuration named 'published' does not exist for project ':services' in task ':services:artifactoryPublish'.
Cannot publish pom for project ':services' since it does not contain the Maven plugin install task and task ':services:artifactoryPublish' does not specify a custom pom path.
Build cache is an incubating feature.
Using local directory build cache for the root build (location = /Users/alexistual/.gradle/caches/build-cache-1).
Using remote HTTP build cache for the root build (pull-only, authenticated = true, url = https://localhost:8081/artifactory/gradle-cache-example/).
 
BUILD SUCCESSFUL in 1s
13 actionable tasks: 7 executed, 5 from cache, 1 up-to-date

Wow! This time, the build only took 1s, which shows the test task was skipped and outputs were taken from the cache instead.

Just to be sure Gradle is not playing around with the local cache, let’s check the Artifactory request.log.

20170526153341|3|REQUEST|127.0.0.1|admin|GET|/gradle-cache-example/6dc9bb4c16381e32ca1f600b3060616f|HTTP/1.1|200|1146
20170526153341|4|REQUEST|127.0.0.1|admin|GET|/gradle-cache-example/e5a67dca52dfaea60efd28654eb8ec97|HTTP/1.1|200|1296
20170526153341|10|REQUEST|127.0.0.1|admin|GET|/gradle-cache-example/24850eca03e013321f28e19ddf421e92|HTTP/1.1|200|1062
20170526153342|3|REQUEST|127.0.0.1|admin|GET|/gradle-cache-example/82d9c0fd6f33462d4b079f0f4a96321e|HTTP/1.1|200|984
20170526153342|3|REQUEST|127.0.0.1|admin|GET|/gradle-cache-example/ad7b7f9a27090e72782121244f3deec2|HTTP/1.1|200|4444

Indeed you can see that the compile and test tasks’ outputs are taken from the Artifactory generic repository we created earlier.

In this example, we’ve shown a simple scenario that gets an impressive but arbitrary 10x improvement. That said, the Gradle team has measured an average reduction of 25% in total build time, and even a reduction of 80% with some of their commits! So, just try it out for yourself to see the possibilities.

What Artifactory brings to the picture

Here’s the best part, if you’re already using JFrog Artifactory for your regular builds, there’s no need to set anything up. To add a remote cache, you just need to create a generic repository, define some permissions and you’re done!

By using Artifactory, you’ll benefit from:

Gradle Build Cache

So don’t let Gradle builds keep you late at the office. Using the new Gradle build cache feature with JFrog Artifactory, your builds will be fast enough to get you out of the office and home in time for dinner.