Multi-release JARs - Good or bad idea?

With Java 9 came a new feature of the Java runtime called multi-release jars. For us at Gradle, it’s probably one of the most controversial additions to the platform. TL/DR, we think it’s a wrong answer to a real problem. This post will explain why we think so, but also explain how you can build such jars if you really want to.

Multi-release JARs, aka MRJARs, are a new feature of the Java platform, included in the Java 9 JDK. In this post, we will elaborate on the significant risks of adopting this technology and provide how one can produce and consume multi-release JARs with Gradle, if desired.

In a nutshell, multi-release jars allow you to package several versions of the same class, for consumption by different runtimes. For example, if you run on JDK 8, the Java runtime would use the Java 8 version of the class, but if you run on Java 9, it would use the Java 9 specific implementation. Similarly, if a version is built for the upcoming Java 10 release, then the runtime would use it instead of the Java 9 and default (Java 8) versions.

Use Cases for multi-release JARs

  • Optimized runtime. This answers a problem that lots of developers have faced in real world: when you develop an application, you don’t know in what runtime it’s going to be executed. However, you know that for some runtimes you can implement optimized versions of the same class. For example, imagine that you want to display the Java version number that your application is currently executed on. For Java 9, you can use the Runtime.getVersion method. However, this is a new method only available if you run on Java 9+. If you target more runtimes, say, Java 8, then you need to parse the java.version property. So you end up with 2 different implementations of the same feature.

  • Conflicting APIs : Another common use case is to handle conflicting APIs. For example, you need to support 2 different runtimes, but one has deprecated APIs. There are currently 2 widely used solutions to this problem:

    • The first one is to use reflection. One could for example define a VersionProvider interface, then 2 concrete classes Java8VersionProvider and Java9VersionProvider, the right one being loaded at runtime (note that funnily to be able to choose between the 2 you might have to parse the version number!). A variant of this solution is just to have a single class, but different methods, accessing and calling different methods by reflection.
    • A more advanced solution would be to use method handles for this, if it is technically applicable. Most likely, you would see reflection as both painful to implement and slow, and you would most likely be right.

Well known alternatives to multi-release JARs

The second solution, easier to maintain and reason about, is to provide 2 different jars, aimed at 2 different runtimes. Basically, you would write the 2 implementations for the same class in your IDE, and it’s the build tool responsibility to compile, test and package them correctly into 2 different artifacts. This is the approach that some tools like Guava or Spock for example have been using for years. But it’s also what some languages like Scala need. Because there are so many variants of the compiler and the runtime, that binary compatibility is almost impossible to maintain.

But there are more reasons to prefer separate jars:

  • a jar is just packaging
    • it’s an artifact of the build that happens to package classes, but not only: resources would typically be bundled into a jar too. Packaging, as well as processing resources, has a cost. What we’re trying to do with Gradle is to improve the performance of builds, and reduce the amount of time a developer has to wait to see results of compilation, tests, and in general the whole build process. By forcing to build a jar too early in the process, you create a redundant point of synchronization. For example, to compile downstream consumers, the only thing the consumer needs is the .class files. It doesn’t need the jar, nor does it need the resources in the jar. Similarly, to execute tests, all Gradle needs is the class files, plus the resources. There’s no need to actually create the jar to execute tests. The jar is only needed once an external consumer will require it (in short, publishing). But as soon as you consider the artifact as a requirement, then you’re blocking some tasks from running concurrently, and you’re slowing down the whole build. While for small projects this might not be an issue, for enterprise scale builds, this is a major blocker.
  • more importantly, as an artifact, a jar shouldn’t carry information about dependencies.
    • There’s absolutely no reason why the runtime dependencies of your Java 9 specific class would be the same as the Java 8 one. In our very simplistic example they would, but for larger project this is wrong modeling: typically, users would import a backport library of a Java 9 feature, and use it to implement the Java 8 version of the class. However, if you package both versions in the same jar, then you’re mixing things that don’t have the same dependency trees into a single artifact. It means, typically, that if you happen to run on Java 9, you’re bringing a dependency that you would never ever use. Worse, it can (and will) pollute your classpath, possibly creating conflicts for consumers.

Eventually, for a single project, you can produce different jars, aimed at different usages:

  • one for the API
  • one for Java 8 runtime
  • one for Java 9
  • one with native bindings

Abuse of the classifier leads to inconsistent things being referred to using the same mechanism. Typically, the sources or javadocs jars are posted as classifiers, but don’t really have any dependency.

  • we don’t want to create a mismatch depending on how you get your classes. In other words, using multi-release jars have the side effect that consuming from a jar and consuming from a class directory are no longer equivalent. There’s a semantic difference between the 2, which is terrible!
  • depending on the tool that is going to create the jar, you may produce inconsistent jars! The only tool so far that guarantees that if you package the same class twice in a jar, both of them have the same public API, is the jar tool itself. Which, for lots of good reasons is not necessarily used by build tools, or even users. A jar, in practice, is just an envelope. It’s a zip in disguise. So depending on how you build it, you would have different behavior, or you could just produce wrong artifacts and never notice.

Better ways to manage separate JARs

The main reason developers don’t use separate jars is that they are impractical to produce and consume. The fault is on build tools, which, until Gradle, have dramatically failed at handling this. In particular, developers who have used this solution had no other choice than relying on the very poor classifier feature of Maven to publish additional artifacts. However, classifiers are very bad at modelling the complexity of the situation. They are used for a variety of different aspects, from publishing sources, documentation, javadocs, to publishing variants of a library (guava-jdk5, guava-jdk7, …) or different usages (api, fat jar, …). And in practice, there’s no way to indicate that the dependency tree of a classifier is not the one of the project itself. In other words, the POM is broken, as it represents both how the component is built, and what artifacts it produces. Say that you want to produce 2 different jars: one classic jar, and one fat jar that bundles all dependencies. In practice Maven would consider that the 2 artifacts have equal dependency trees, even if it’s plain wrong! It’s super obvious in this case, but the situation is exactly the same with multi-release jars!

The solution is to handle variants properly. That’s what we call variant-aware dependency management, and Gradle knows how to do it. So far this feature has only been enabled for Android development, but we’re currently developing it for Java and native too!

Variant-aware dependency management is the idea that modules and artifacts are different beasts. With the same source files, you can target different runtimes, with different requirements. For the native world it has been obvious for years: we compile for i386 and amd64, and there’s no way you can mix the dependencies of a i386 library with the ones of arm64! Transposed to the Java world, it means that if you target Java 8, you should produce a java 8 version of your jar, with classes targeting the Java 8 class format. This artifact would have metadata attached so that Java 8 consumers know what dependencies to use. And if you target Java 9, then the Java 9 dependencies would be selected. It’s as simple as that (well, in practice it’s not because the runtime is only one dimension of the variants, and you can combine multiple).

Of course, nobody has ever done this before because it’s complex to handle: Maven would for sure never let you do such complex thing. But Gradle makes it possible. And the good news is that we’re also developing a new metadata format that will let consumers know which variant they should use. Simply said, the build tool needs to deal with the complexity of compiling, testing, packaging, but also consuming such modules. For example, say that you want to support Java 8 and Java 9 as runtimes. Then, ideally, you need to compile 2 versions of your library. Which means 2 different compilers (to avoid using the Java 9 APIs while targeting Java 8), 2 different class directories, and 2 different jars in the end. But also, you will probably want to test the 2 different runtimes. Or, you might want to build the 2 jars, but still want to test what the behavior of the Java 8 version is when executed on a Java 9 runtime (because, it may happen in production!).

We’ve made significant progress towards modelling this, and even if we’re not ready yet, it explains why we are not so keen on using multi-release jars: while they fix a problem, they are fixing it the wrong way, and Maven Central is going to be bloated with libraries that do not declare their dependencies properly!

How to create a multi-release JAR with Gradle

It’s not ready so what should I do? The good news is that the path to generate correct artifacts is the same. Until this new feature is ready for the Java ecosystem, you have 2 different options:

  • do it the old way, using reflection or distinct jars.
  • use multi-release jars, (being aware that you may take the wrong decision here, even if there are good use cases)

Whatever solution you choose, separate jars route or the multi-release jars, both use the same setup. Multi-release jars are only the wrong (default) packaging: they should be an option, not a goal. Technically, the source layout is the same for both separate jars and external jars. This repository explains how you can create a multi-release jar with Gradle, but here is how it works in a nutshell.

First, you must understand that we as developers often have a very bad habit: we tend to run Gradle (or Maven) using the same Java version that the artifacts you want to produce. Sometimes it’s even worse, when we use a more recent version to run Gradle, and compile using an older API level. But there’s no good reason to do this. Gradle supports cross-compilation. It allows you to explain where a JDK is found, and fork compilation to use this specific JDK to compile a component. A reasonable way to setup different JDKs is to configure the path to the JDKs through environment variables, which is what we are doing in this file. Then we only need to configure Gradle to use the appropriate JDK based on the source/target compatibility. It’s worth noting that starting from JDK 9, it’s no longer necessary to provide older JDKs to perform cross-compilation. A new option, -release, does exactly that. Gradle will recognize this option and configure the compiler accordingly.

The second key concept is the notion of source set. A source set represents a set of sources that are going to be compiled together. A jar is built from the result of the compilation of one or more source sets. For each source set, Gradle will automatically create a corresponding compile task that you can configure. This means that if we have sources for Java 8 and sources for Java 9, then they should live in separate source sets. That’s what we do by creating a Java 9 specific source set that will contain the specialized version of our class. This matches reality, and doesn’t force you to create a separate project like Maven would require. But more importantly, it allows us to precisely configure how this source set is going to compile.

Part of the challenge of multiple versions of a single class is that it’s very rare that such a class is totally independent from the rest of the code (it has dependencies onto classes that are found in the main source set). For example, its API would use classes that don’t need to have Java 9 specific sources. Yet, you don’t want to recompile all those common classes, nor do you want to package Java 9 versions of all those classes. They are really shared and should stay separate. This is what this line is about: it will configure the dependency between the Java 9 source set and the main source set, making sure that when we compile the Java 9 specific version, all common classes are on compile classpath.

The next step is really simple: we need to explain to Gradle that the main source set is going to target Java 8 language level, and that the Java 9 source set is going to target Java 9 language level.

All the steps we have described so far allow you both approaches described previously: publishing separate jars, or publishing a multi-release jar. Since this is the topic of this blog post, let’s see how we can now tell Gradle that we will only generate a multi-release jar:

jar {
  into('META-INF/versions/9') {
     from sourceSets.java9.output

     'Multi-Release': 'true'

This configuration block does 2 separate things: bundle the Java 9 specific classes into the META-INF/versions/9 directory, which is expected in a MRJar add the multi-release flag to the manifest.

And that’s it, you’ve built your first MRJar! However we’re not done yet, unfortunately. If you are familiar with Gradle, you would know that if you apply the application plugin you can also run the application directly with a run task. However, because as usual Gradle tries to perform the minimal amount of work to do what you need, the run task is wired to use the class directories as well as the processed resources directories. And for multi-release jars, that’s a problem, because you need the jar now! So instead of relying on this plugin we have no choice but creating our own task, which is another reason why not use multi-release jars.

Last but not least, we said we probably also want to test the 2 versions of our class. For this, you have no choice but using forked VMs, because there’s no equivalent to the -release flag for the Java runtime. The idea, here, is that you write a single unit test, but it’s going to be executed twice: once with Java 8, the other with Java 9 runtime. This is the only way to make sure that your substituted classes work properly. By default, Gradle only creates a single test task, and it will also use the class directories instead of the jar. So we need to do two things: create a Java 9 specific test task configure both test tasks so that they use the jar and specific Java runtimes

This can be achieved simply by doing this:

test {
   dependsOn jar
   def jdkHome = System.getenv("JAVA_8")
   classpath = files(jar.archivePath, classpath) - sourceSets.main.output
   executable = file("$jdkHome/bin/java")
   doFirst {
       println "$name runs test using JDK 8"

task testJava9(type: Test) {
   dependsOn jar
   def jdkHome = System.getenv("JAVA_9")
   classpath = files(jar.archivePath, classpath) - sourceSets.main.output
   executable = file("$jdkHome/bin/java")
   doFirst {
       println classpath.asPath
       println "$name runs test using JDK 9"



Now if you run the check task, Gradle will compile each source set using the proper JDK, build a multi-release jar, then run unit tests using this jar on both JDKs. Future versions of Gradle will help you do this in a more declarative way.


In conclusion, we’ve seen that multi-release jars address a real problem that a significant number of library designers face. However, we think this is the wrong solution to the problem. Correct modeling of dependencies, as well as coupling of artifacts and variants, and not forgetting performance (ability to execute more tasks concurrently) make them a poor man’s solution to a problem we are fixing the right way, using variant-aware dependency management. However, we reckon that for simple use cases, knowing that variant-aware dependency management for Java is not yet completed, it may be convenient to produce such a jar. In that case, and only in that case, this post helped you understand how you can do this, and how the philosophy of Gradle differs from Maven in this case (source set vs project).

Finally, we don’t deny that there are cases where multi-release jars do make sense: applications for which the runtime is not known in advance, for example, but those are exceptional and should be considered as such. Most issues are for library designers: we’ve covered common problems they face, and how multi-release JARs attempt to solve some of them. Modeling dependencies correctly as variants improves performance (via finer-grained parallelism) and reduces maintenance overhead (avoiding accidental complexity) over the use of multi-release JARs. Your situation may dictate that MRJARs be used; rest assured that it’s still supported by Gradle. See this mrjar-gradle example project to try this today.

State of Gradle Java 9 Support

This post provides an overview of Gradle’s Java 9 support, touching on runtime, cross-compilation, MRJARs, and Jigsaw modules support. We’ve fielded lots of questions since Java 9 was released last month, and decided it best to answer here.

What Gradle supports as of version 4.2.1

As of Gradle 4.2.1, building and running Java applications using major distributions of JDK 9 such as Oracle JDK9, OpenJDK9 and Azul JDK9 is fully supported. Further, cross-compilation (built by JDK9 but runs on JDK8) is supported.

Some builds will break when upgrading to Java 9, regardless of build tool used. The Java team have made good and necessary changes to the JDK to facilitate better software architecture and security, but this has meant removing access to some APIs. Even if your project is ready, some tools and Gradle plugins have not yet been updated to work with Java 9.

There is no convenience methods for consuming and assembling Multi-Release JARs, but you can take a look at this MRJAR-gradle example if you desire to use them.

Java Modules aka Jigsaw Support

If you’re not yet familiar with the Java 9 Platform Module System, also known as Project Jigsaw, you should read Project Jigsaw: Module System Quick-Start Guide. The motivation and terminology are well explained in The State of the Module System.

A module is defined as “a named, self-describing collection of code and data” whereby packages are treated as code boundaries, and are explicitly exported and required. Non-exported packages are not visible to module consumers, and further 2 modules cannot export the same packages, nor can they have the same internal packages. This means that packages cannot be “split” or duplicated between multiple modules, or compilation will fail.

Here is a guide that shows how to use Java modules with Gradle today. It walks you through the steps necessary to tell Gradle to use the modulepath and not classpath when compiling Java sources and patch modules for testing purposes.

A bottom-up approach (convert libraries with no dependencies first) is recommended if you wish to incrementally convert to Java 9 modules. After all, modules are consumable as regular JARs. Be mindful of automatic modules when “legacy” JARs are added to the modulepath.

Achieving encapsulation with the Java Library Plugin

One of the 2 major goals of the Java 9 module system is to provide better software architecture through strong encapsulation. Gradle 3.4 introduced the Java Library Plugin that enforces strong encapsulation for libraries by separating api dependencies (those meant to be exposed to consumers) from implementation dependencies whose internals are not leaked to consumers.

This, of course, does not eliminate use of Java classpaths as is stated as another goal of Java modules. You can learn about the motivation and usage of the Java Library Plugin in this post. It’s worth noting that the Java Library Plugin is useful for projects using Java 7 and above — you do not need to migrate to Java 9 to have some stronger encapsulation.

Here’s what this means in practice, given this example library:

apply plugin: 'java-library'

name = 'mylibrary'
group = 'com.mycompany'

dependencies {
   api project(':model')
   implementation ''

Let’s presume we have an application that uses mylibrary.

public class MyApplication {
    public static void main(String... args) {
        // This does not compile using 'java-library' plugin
        Set<String> strings ="Hello", "Goodbye");
        // This compiles and runs
        Foo foo = com.mycompany.model.internal.Foo();
        // This also compiles and runs
        Class clazz = MyApplication.class.getClassLoader().loadClass("com.mycompany.model.internal.Foo");
        Foo foo = (Foo) clazz.getConstructor().newInstance();

You can see that you get some of the benefits by adopting the Gradle’s Java Library plugin. If you are migrate to Java modules, you can use this rough mapping:

  • implementation dependency => requires module declaration
  • api dependency => requires transitive module declaration
  • runtimeOnly dependency => requires static module declaration

Next Steps

Stay tuned for updates on first-class Java modules support in Gradle.

You can use the Building Java 9 Modules guide to learn how to use Java modules with Gradle today.

The Road to Gradle Script Kotlin 1.0

Five months ago we announced the first pre-release of Gradle Script Kotlin, and we thought now would be a good time to review the progress we’ve made since. We have shipped eight additional pre-releases during that time, and the road to 1.0 is looking clearer every day. So let’s take a look at the ground we’ve covered so far and where we’re going from here, shall we?


As you may recall, this is what our hello-world sample looked like at the time of our first release:

import org.gradle.api.plugins.*
import org.gradle.script.lang.kotlin.*


configure<ApplicationPluginConvention> {
    mainClassName = "samples.HelloWorld"

repositories {

dependencies {

Oh, that annoying org.gradle.script.lang.kotlin.* import! The publicly condemned, IDE unfriendly, string-based "testCompile" dependency configuration! And of course—for those souls brave enough to have tried them—the infamous generateKtsConfig and patchIdeaConfig tasks required to get Kotlin-based build scripts working in IDEA. These were early days, no doubt, and they brought with them a few rough edges.

But despite its flaws, the programming language and IDE experience in 0.1.0 was already so good it got us hooked. As for the rough edges, we could already see ways to smooth them out, which led to the release of 0.2.0 one month later.


With implicit imports and a tooling-friendly alternative to string-based dependency configurations, hello-world 0.2.0 started looking clean and concise:


configure<ApplicationPluginConvention> {
    mainClassName = "samples.HelloWorld"

repositories {

dependencies {

Seamless project imports meant that Kotlin-based builds in IDEA started working out of the box, and the days of mistyping generateKtsConfig and patchIdeaConfig were no more.

Perhaps most importantly, 0.2.0’s support for build script dependencies and external plugins made Gradle Script Kotlin a viable choice for many real-world projects.


0.3.0 was a major milestone for the project, as it was the first version to be included in a production Gradle distribution—Gradle 3.0, no less!

And 0.3.0 was all about that Kotlin! The new Kotlin 1.1-M01 compiler, support for Kotlin-based plugins and buildSrc directories plus some sugary Kotlin-Groovy interoperability primitives:

gradle.buildFinished(closureOf<BuildResult> {
    println("$action finished") // $action refers to BuildResult.getAction()

With Gradle 3.0 out the door, the #gradle channel of the public Kotlin Slack saw an increase in participation which helped us greatly in prioritizing the work that would come next.


We noticed people struggling with the lack of a more type-safe and IDE-friendly way of configuring dependencies, so 0.3.1 came with a much-improved dependencies DSL:

dependencies {

    default(group = "org.gradle", name = "foo", version = "1.0") {
        isForce = true

    compile(group = "org.gradle", name = "bar") {
        exclude(module = "foo")

    runtime("org.gradle:baz:1.0-SNAPSHOT") {
        isChanging = true
        isTransitive = false

    testCompile(group = "junit", name = "junit")

    testRuntime(project(path = ":core")) {
        exclude(group = "org.gradle")

The upgrade to Kotlin 1.1-dev-2053 notably enhanced the performance of code assistance within IDEA and thanks to a valuable member of the community the first Gradle Script Kotlin Android sample was published.


With 0.3.2 we decided to tackle the dreaded it problem head-on via runtime code generation of Kotlin extensions. What is the dreaded it problem? Take the use of copySpec as an example. Prior to 0.3.2, one would have written:

copySpec {

This syntax didn’t read very well, and was a departure from the fluid, readable DSL Gradle has long been known for. But never fear—with 0.3.2 it was gone:

copySpec {

v0.3.3 and v0.4.0

The recently-released versions 0.3.3 and 0.4.0 shipped the first of a series of improvements to multi-project builds including the ability to define custom build logic using Kotlin in buildSrc.

0.4.0 is available now and will ship with the forthcoming Gradle 3.2 distribution.

Toward v1.0.0

What’s next, you ask? Here are some of the highlights of our upcoming releases in three key areas:

  1. Performance: Faster project configuration via caching of compiled build scripts (#31).
  2. Usability: Type-safe accessors for extensions and conventions contributed by plugins (#159); Comprehensive documentation (#106).
  3. Convenience: Declarative and tooling-friendly application of plugins, a.k.a., the plugins block (#168).

Altogether, here’s how we envision the hello-world sample will look in Gradle Script Kotlin 1.0:

plugins {

application {
    mainClassName = "samples.HelloWorld"

repositories {

dependencies {

How does that look to you? We’d love to hear what you think.

A big thanks to everyone that’s been along for the ride so far, and if you’re just getting started with Gradle Script Kotlin, welcome!

Now Open: GitHub Issues for Gradle

Gradle has been an open-source project since its inception, but as a team we haven’t always lived up to the spirit of modern open-source collaboration. For example, we haven’t made it easy for folks to stay abreast of what we’re working on, and we haven’t had a clear and simple process for users to submit feature requests or bugs against a proper issue tracker.

We’re very happy to announce that all of that is changing today. We’ve opened up GitHub Issues on the Gradle repository, and what follows are the immediate, medium-, and long-term changes we’re making to put—and keep!—the needs of the Gradle community front and center.

Effective Immediately

✔︎ You can now submit feature requests and bugs through GitHub Issues. We’ve put together a simple set of guidelines in the form of an issue template so you’ll be presented with them each time you submit a new issue. We’ll label issues that don’t follow these guidelines as not-actionable and we’ll add a comment asking for what’s missing. To keep things clean, we’ll close these issues after a week of inactivity.

✔︎ Actionable issues you submit will be prioritized alongside other Gradle improvements. Low-cost changes that benefit a large number of users are the easiest to justify, so please add a 👍  reaction to issues that matter to you. Like many other GitHub projects, we’ll use these reactions as a simple kind of voting system.

✔︎ We’ll manage issue priority and workflow using ZenHub. If you’re not already familiar, ZenHub adds a number of features to GitHub Issues through its clever browser extension. It allows us to group related issues into epics and to visualize everything via a kanban board. Installing and using ZenHub is by no means a requirement, but do check it out if you’d like to get an additional level of insight about what we’re working on.

✔︎ Pull requests will be acknowledged within a week and reviews of them will be done through GitHub’s built-in pull request reviews. Please continue to follow the contribution guidelines—in a project as large and widely-used as Gradle, it’s crucial to be rigorous and get things right.

Medium-Term Transition

Open JIRA issues will be migrated to GitHub, and no new issues will be put into JIRA after the release of Gradle 3.2. We’ll ask for examples and/or clarification on JIRA issues that are not actionable so that they can be prioritized.

To draw a clearer roadmap, we’ll add and maintain high-level feature epics and mark them with milestones representing future Gradle releases.

Our bugs forum will no longer be necessary and will be made read-only. For general usage questions and potential bugs for which no reproducible test case or build scan can be provided, please continue to use the help/discuss forum.

Long-Term Unity

Once we’ve made the changes detailed above, we’ll continue looking for ways we can open up Gradle ecosystem to even greater community contribution. We’re really excited about this, and we hope you will be too.

We’d love to hear your feedback—let us know what you think in the comments on this post or talk to us @Gradle on Twitter. Your suggestions, questions, and of course, issues are most welcome.

Hello, Again

Welcome to the new Gradle blog. On behalf of the whole team, it’s my pleasure to write this first post and share a bit about what we’re up to.

Mainly, we’re putting together this new blog because we want to make sure users find out about the most important developments in Gradle-land. Our team has grown rapidly over the last couple years, and as a result we’ve shipped many new features and improvements. All too often, though, we find that would-be users never hear about them. This recent tweet provides a perfect example:

Cristian’s question is a fair one. We first shipped Play support over a year ago; we mentioned it in our Gradle 2.6 forum announcement and release notes, and we wrote a chapter about it in our user manual. Still, Cristian—and probably many others—missed it. How is that?

The answer is pretty straightforward. Forum announcements and release notes are useful documents, but they get buried as new releases pile up. Reference documentation is important too, but our user manual has grown large, meaning that Chapter 70: Building Play Applications is easy to miss if you’re not already looking for it.

This situation isn’t surprising, nor is it unique to Gradle. As any project grows, it becomes a challenge to communicate about it effectively. In any case, we can no longer expect every current and future Gradle user to dig through past release notes or to read our user manual cover to cover simply to discover what Gradle can do. It’s on us to find better ways to get the word out.

And that’s where this new blog comes in. We’ll post here whenever there’s something new in Gradle that we don’t want you to miss. We’ll keep it focused on things we think are important and worth your time. We hope it’ll become a trusted resource—not only for you to stay up to date with Gradle, but also for us to get feedback through your comments.

A better blog isn’t a silver bullet, but we think it’s a great place to start. Indeed, it’s just the first step in a much larger effort to make working with and learning about Gradle as easy and enjoyable as possible. In the weeks to come you’ll see further improvements, including new guides and tutorials, a new website, and a simpler process for filing bugs and feature requests.

Naturally, we’ll announce each of these changes here on the blog. To stay tuned, subscribe to the blog’s Atom feed or follow @Gradle on Twitter, where we’ll link to new posts as we publish them.

Gradle 3.0 M1: Unleash the Daemon!

The first milestone release toward Gradle 3.0 has just been published, and among many smaller improvements, it contains two major features we’d like to get your feedback on.

The first feature is support for writing Gradle build scripts in Kotlin, and you can read all about it in last month’s Kotlin meets Gradle blog post. While still in the early stages of development, this functionality is now available out of the box with Gradle 3.0 M1, and we’d love to hear what you think. See the Gradle Script Kotlin 0.1.0 release notes for complete details and getting started instructions.

The second feature is that the Gradle Daemon is now enabled by default. This is a big deal, and I want to focus on it for the rest of this post.

What is the Gradle Daemon?

The Gradle Daemon is a long-running background process—a “warm JVM”—that Gradle can connect to from the command line or from within your IDE. The daemon keeps track of all kinds of information about your project’s build and makes that information available instantaneously. The result is that Gradle doesn’t have to recalculate all that information each time you run gradle build or gradle test or any other task. And the benefit to you, of course, is that your build times get a whole lot faster.

The daemon has been available since way back in Gradle 0.9, but until now it’s been an opt-in feature, meaning that users have had to provide the --daemon option at the command line or otherwise set Gradle’s org.gradle.daemon property to true in order to take advantage of it. This approach worked perfectly well, but only if you knew about it. And while many Gradle users do know about the daemon, and make use of it every day, we know from interacting with our community that the majority of Gradle users do not yet take advantage of the daemon.

So if you haven’t been familiar with the daemon until now, or if you’ve struggled to make your colleagues aware of this feature, don’t worry—you’re not alone. It’s for these reasons that we’ve made enabling the daemon by default a key part of Gradle 3.0.

Unleashing the Daemon

Over the years, the daemon has become an advanced and highly capable piece of software. But enabling it by default isn’t as simple as just “flipping a switch”. This change is profound—it means that from 3.0 forward, every Gradle user will interact with the daemon on every build unless they explicitly opt out. That’s a whole lotta builds, and with numbers like that, it means that even the tiniest bugs and most obscure corner cases are likely to surface where they haven’t before. This is why we’ve invested significant engineering effort over the last several months to ensure the daemon is up to this new challenge—and it’s why we’re so eager to get your feedback on this first milestone.

Trying out 3.0 M1 and its auto-enabled daemon is easy. Just update your Gradle wrapper using the following command and then run your builds as you normally would:

gradle wrapper --gradle-version 3.0-milestone-1

And of course, if you have any problems, comments or questions, let us know!

The Daemon in action

The short asciicast below demonstrates upgrading a project’s build from Gradle 2.13 to 3.0 M1 and shows off the resulting performance gains side-by-side in a before-and-after comparison. As you’ll see, the results are pretty dramatic—the project’s clean build time is cut by more than half:

There’s more performance goodness to come

If you run Gradle builds at all, there’s a good chance you run them dozens of times every day. This means that every second counts, and that’s why enabling the daemon by default is just one of a number of performance-related efforts we’ve been up to. Indeed, consistent with the other meaning of the word daemon, you might say we’ve been hell-bent on making Gradle the fastest and most capable build system in the world. There’s plenty more work to do to achieve that goal, and your feedback will help every step of the way. To that end, we hope you’ll check out the 3.0 M1 release notes, try it out for yourself, and let us know how it goes. Thanks!