Provided Dependencies in Gradle

To this day Gradle is lacking a so called provided scope. This scope is probably best known from Maven which offers it out of the box. It allows to set dependencies which are provided by the runtime environment the program will later run on. Examples for such a runtime environment could be an application server like Wildfly or a distributed computing platform like Hadoop. Dependencies which are classified as provided are still needed to compile, yet you don’t need and don’t want them to ship in your deployable package. You don’t need them because they will be in your target environment anyway. And you don’t want them to ship to avoid conflicts of different versions of the same library.

To be honest, Gradle does not totally lack provided. The war plugin offers providedCompile and providedRuntime. So when you are building a web application you can just use that.

In case you build a jar you need to handcraft a provided scope. There haven been discussions about that already. If you’re interested in progress of that matter you can watch GRADLE-784.

The basis for the following code was a nice article by Mick Brooks. Here we go:

apply plugin: 'java'

repositories {
    mavenCentral()
}

configurations {
    provided
}

sourceSets {
    main {
        compileClasspath += configurations.provided
        runtimeClasspath += configurations.provided
    }
    test {
        compileClasspath += configurations.provided
        runtimeClasspath += configurations.provided
    }
}

dependencies {
     provided '...'
}

The fundamental idea is to create a new configuration and add it to all classpaths of main and test. There are other smaller variations of this code out there. They tend to change the compileClasspath of main only. This will work most of the time. It depends on your needs. For example, when you write integration tests where you need the provided dependencies you have to add them to your tests, too.

But why add it to the runtimeClasspath? Because there are relationships between runtime and compile configurations. The runtime configuration extends compile which means that any dependency in compile also appears in runtime. The two classpath properties use the respective configurations. So compileClasspath uses the compile configuration and runtimeClasspath uses runtime configuration. For short: I consider it good practice to add provided to both compileClasspath and runtimeClasspath because it would naturally happen with dependencies anyway. If your needs differ you can still remove these.

Our favorite build tool is now set up. How about our IDE? I like to generate the correct dependencies from our single source of truth – the build script. For IntelliJ IDEA this is achieved by:

apply plugin: 'idea'

idea {
    module {
        scopes.PROVIDED.plus += [ configurations.provided ]
    }
}
apply plugin: 'java'

repositories {
    mavenCentral()
}

configurations {
    provided
}

sourceSets {
    main {
        compileClasspath += configurations.provided
        runtimeClasspath += configurations.provided
    }
    test {
        compileClasspath += configurations.provided
        runtimeClasspath += configurations.provided
    }
}

dependencies {
     provided '...'
}

apply plugin: 'idea'

idea {
    module {
        scopes.PROVIDED.plus += [ configurations.provided ]
    }
}