Background
For a long time we have been using compileOnly for production and implementation for development (for IDE support). We use ShadowJar plugin to generate the application jar file. All the dependencies are stored in a lib directory and linked in the classpath.
The reason behind that way of working is time. Its just faster to compile and to deploy. For example, one of our applications takes like 5 minutes to compile when using implementation in the project dependencies against few seconds when using compileOnly. Then, the resulting file when using implementation is about 800MB, while its under 1MB when using compileOnly (as all the dependencies are just copied to a directory without creating a “fat jar”). Using rsync to the target server is much more convenient than copying each time a 800MB file (specially under slow networks).
Problem Description
One of our libraries uses implementation for those dependencies which are no directly required by the application. For example, the library wraps gson library but it doesn’t expose any of its classes to the consumer.
The problem is that when using compileOnly, those libraries are not copied into the lib directory resulting in a NoClassDefFoundError exception. That doesn’t happen when we create a “fat jar” using implementation.
Question
Is there a way to include those libraries as well ? configuration.compileClasspath doesn’t include those libraries. Are we doing something wrong?
build.gradle (interesting parts)
Gradle version: 7.1.1
// Using ShadowJar Plugin:
plugins {
id 'groovy'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
// Using compileOnly to reduce jar size
dependencies {
compileOnly "com.example.library:module:1.0.0"
... many more ...
}
// Copying all the dependencies into "lib" directory
task copyRuntimeLibs(type: Copy) {
into "lib"
from configurations.compileClasspath
}
// Basic ShadowJar configuration:
shadowJar {
archiveBaseName.set(project.name)
archiveVersion.set(project.version.toString())
archiveClassifier.set("linux-x86_64")
zip64 = true
shadowJar.finalizedBy copyRuntimeLibs
}
// Linking all the libraries under "lib" to the class path:
jar {
from configurations.runtimeClasspath.collect { zipTree it }
manifest {
attributes(
'Implementation-Version': archiveVersion,
'Main-Class': "${systemMainClass}",
"Class-Path": configurations.compileClasspath.collect { 'lib/' + it.name }.join(' ')
)
}
exclude 'META-INF/*.RSA', 'META-INF/*.DSA', 'META-INF/*.SF'
}