Speed Up Your Gradle Builds with JFrog Artifactory
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.
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:
- Access control by managing permissions according to team/project/tools. For example, you can provide deploy permission to the CI server users and read permission to developers.
- Advanced cleanup of those repositories is made easy and convenient using JFrog CLI and Artifactory Query Language (AQL).
- A distributed cache that’s synchronized across both local and remote teams using push and pull repository replication so that not only will your Gradle builds be faster, your colleagues’ builds across the ocean will also be faster.
Learn more about replication across different sites in our white paper on multi-site topologies.
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.