Brings improvements to the Java ecosystem

Gradle 3.4 improves support for building Java applications

Gabriela Motroc

Gradle 3.4 comes bearing gifts: compile avoidance, boosted incremental compilation, Java Library plugin and more. In short, it brings improvements to the Java ecosystem.

Gradle 3.4 is here. Cédric Champeau, core developer at Gradle, announced in a blog post that the improvements made can dramatically improve users build times. Here’s what they measured:

What’s new in Gradle 3.4?

According to Champeau, “one of the greatest changes in Gradle 3.4 regarding Java support just comes for free: upgrade to Gradle 3.4 and benefit from compile avoidance.” This is what Gradle calls compilation avoidance:

Imagine that your project app depends on project core, which itself depends on project utils:

In app:

public class Main {
   public static void main(String... args) {
        WordCount wc = new WordCount();
        wc.collect(new File(args[0]);
        System.out.println("Word count: " + wc.wordCount());
   }
}
In core:
public class WordCount {  // WordCount lives in project `core`
   // ...
   void collect(File source) {
       IOUtils.eachLine(source, WordCount::collectLine);
   }
}
In utils:
public class IOUtils { // IOUtils lives in project `utils`
    void eachLine(File file, Callable<String> action) {
        try {
            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                // ...
            }
        } catch (IOException e) {
            // ...
        }
    }
}

Then, change the implementation of IOUtils. For example, change the body of eachLine to introduce the expected charset:

ublic class IOUtils { // IOUtils lives in project `utils`
    void eachLine(File file, Callable<String> action) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8") )) {
                // ...
            }
        } catch (IOException e) {
            // ...
        }
    }
}

Now rebuild app. Until now, utils had to be recompiled, but then it also triggered the recompilation of core and eventually app, because of the dependency chain.

Champeau explained that what changed in IOUtils is purely an internal detail. Although the implementation of eachLine changed, its public API didn’t. Any class file previously compiled against IOUtils is still valid. Therefore if you make such a change, Gradle will only recompile utils.

Even when the compilation cannot be avoided, Gradle 3.4 will still make things much faster thanks to incremental compile.

Incremental compilation

Gradle 3.4 makes everything easier. Case in point: to enable Java incremental compilation, all you need to do is to set it on the compile options:

tasks.withType(JavaCompile) {
   options.incremental = true // one flag, and things will get MUCH faster
}

If we add the following class in project core:

public class NGrams {  // NGrams lives in project `core`
   // ...
   void collect(String source, int ngramLength) {
       collectInternal(StringUtils.sanitize(source), ngramLength);
   }
   // ...
}

and this class in project utils:

public class StringUtils {
   static String sanitize(String dirtyString) { ... }
}

The incremental compiler analyzes the dependencies between classes, and only recompiles a class when it has changed, or one of the classes it depends on has changed, Champeau said. It is also backed with in-memory caches that live in the Gradle daemon across builds, and thus make it significantly faster than it used to be: extracting the ABI of a Java class is an expensive operation that used to be cached but on disk only.

Java Library plugin

The Java Library plugin expands the capabilities of the Java plugin by providing specific knowledge about Java libraries. Starting from Gradle 3.4, if you build a Java library, you should use the new Java Library plugin. Instead of writing:

apply plugin: 'java'

use:

apply plugin: 'java-library'

The advantage of the java-library plugin is that it exposes the concept of an API.

According to the documentation, the aim of the Java Library plugin is to expose two configurations that can be used to declare dependencies: api and implementation. The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.

Check out all the improvements and features here.

What’s next for Gradle?

There’s more to come, Champeau warned. Since the separation of API and implementation is key to Java 9 success, with the awakening of Project Jigsaw,  Gradle will add a way to declare what packages belong to your API, making it even closer to what Jigsaw will offer, but supported on older JDKs too. Furthermore, Gradle 4.0 will ship with a build cache.

But let’s enjoy Gradle 3.4 for now.

Author
Gabriela Motroc
Gabriela Motroc is editor of JAXenter.com and JAX Magazine. Before working at S&S Media she studied International Communication Management at The Hague University of Applied Sciences.

Comments
comments powered by Disqus