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 'com.google.guava:guava:18.0'
}

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 = com.google.common.collect.ImmutableSet.of("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?

v0.1.0

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.*

apply<ApplicationPlugin>()

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

repositories {
    jcenter()
}

dependencies {
    "testCompile"("junit:junit:4.12")
}

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.

v0.2.0

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

apply<ApplicationPlugin>()

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

repositories {
    jcenter()
}

dependencies {
    testCompile("junit:junit:4.12")
}

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.

v0.3.0

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.

v0.3.1

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.

v0.3.2

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 {
    it.from("src/data")
    it.include("*.properties")
}

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 {
    from("src/data")
    include("*.properties")
}

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
}

application {
    mainClassName = "samples.HelloWorld"
}

repositories {
    jcenter()
}

dependencies {
    testCompile("junit:junit:4.12")
}

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 gradle.org 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!