Exclude RuntimeOnly dependencies from installDist task

Maybe I’m not using Gradle will enough, but I have a dependency configuration where I have

dependencies {
    javaagent("com.datadoghq:dd-java-agent:1.16.0")
    implementation(fileTree("lib") { include("*.jar") })

    implementation("org.springframework.boot:spring-boot-starter-web") {
        exclude("org.springframework.boot", "spring-boot-starter-tomcat")
    }
    implementation("org.springframework.boot:spring-boot-starter-webflux")

    ...


    // testing dependencies
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude(module = "mockito-core")
    }
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation("io.kotest:kotest-property:5.6.2")
    testImplementation("com.ninja-squad:springmockk:4.0.2")
    testImplementation("com.maciejwalkowiak.spring:wiremock-spring-boot:1.0.1")

    // local development tools
    runtimeOnly("org.springframework.boot:spring-boot-devtools")
    runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64")

    // http://localhost:3000/actuator/prometheus
    runtimeOnly("io.micrometer:micrometer-registry-prometheus")
}

And when I run the installDist task the lib directory includes the runtimeOnly dependencies.

Is there any way I can exclude these runtimeOnly dependencies from the distribution lib directory?

runtimeOnly dependencies are dependencies that your application needs at runtime, but not for compilation.
An example for a runtimeOnly dependency is log4j-core if you only configure it through configuration file and do not use it in your code.

If you want to exclude the runtimeOnly dependencies from installDist result, then you are most probably misusing runtimeOnly and should instead fix that, as it then is also in other places where you don’t want it.

I don’t have any configuration for installDist, nor do I have any configuration for runtimeOnly dependencies.
Unless the spring boot or kotlin plugins are doing anything to the setup, I’m not sure what misuse is happening.

You most probably apply the application plugin, which among other things configures the installDist task.

That the runtimeOnly dependencies are put to the lib folder is not the misuse. That is perfectly fine and exactly what runtimeOnly is for as I explained above.

If you declare dependencies as runtimeOnly that you do not want in the lib folder and thus not available at runtime, that would be the misuse. :slight_smile:

1 Like

What should I declare the dependencies as?

What are they for?
Where should they land?
What do you need them for?

I need those runtimeOnly dependencies for local development, they need to be available when I run the application locally, and they should not be included in installDist.

Well, the right solution heavily depends on the details.
If you for example use the run task for the “local development run”, then you should probably create a new configuration, declare those dependencies on that configuration and then wire it into the run task.

I tried adding

val runOnly: Configuration by configurations.creating

dependencies {
    // local development tools
    runOnly("org.springframework.boot:spring-boot-devtools")
    runOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64")

    // http://localhost:3000/actuator/prometheus
    runOnly("io.micrometer:micrometer-registry-prometheus")
}

tasks.named("run") {
     ...
    dependsOn(runOnly.dependencies)
}

But the dependency wiring isn’t working, maybe I need to wire it differently?

I was able to wire it with

tasks.named("run") {
    ...
    configurations {
        runtimeClasspath.configure {
            extendsFrom(runOnly)
        }
    }
}
val runOnly: Configuration by configurations.creating

is not so good, it creates a configuration with deprecated settings due to backwards compatibility.
Better do

val runOnly: Configuration by configurations.creating {
    isCanBeResolved = true
    isCanBeConsumed = false
    isVisible = false
}

and maybe name it devRuntimeOnly or similar. runOnly can be easily confused with runtimeOnly.

tasks.named("run") {
    ...
   dependsOn(runOnly.dependencies)
}

This is not too helpful, it just makes sure that all the dependencies declalred in runOnly are downloaded or built, but you don’t use them anywhere. Besides that practically any dependsOn that does not have a lifecycle task on lhe lefthand side is not the right thing to do.

I was able to wire it with

tasks.named("run") {
   ...
   configurations {
       runtimeClasspath.configure {
           extendsFrom(runOnly)
       }
   }
}

Actually, no.
The configurations block you have there within the run configuration does not configure the run task. If you would want to have that, you should have it outside the run configuration. But that block is anyway not what you want, because you now just duplicated the runtimeOnly configuration. You configured the runtimeClasspath to also have all dependencies you declare on runOnly, so they will again land in your final distribution.

What you want should be something like

tasks.named<JavaExec>("run") {
    classpath(runOnly)
}
1 Like

Thanks! That was a great help!

1 Like