Gradle 5.6.4 This happens only for parallel build and it's quite difficult to get these outputs because of the race condition. The caching option has no impact on this issue. I wouldn't be able share the whole multi modular project, so I tried to sample one. It runs on 12 logical processors. I've noticed this issue a long time ago, however, the new versions of Gradle or Spring Boot didn't help.
build.gradle.kts
plugins {
\`java-library\`
application
id("io.spring.dependency-management") version "1.0.9.RELEASE"
id("org.springframework.boot") version "2.1.12.RELEASE"
}
allprojects {
apply(plugin = "application")
apply(plugin = "io.spring.dependency-management")
apply(plugin = "org.springframework.boot")
application {
mainClassName = "example.Application"
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
springBoot {
buildInfo()
}
}
subprojects {
dependencies {
implementation(project(":"))
}
}
gradle.properties
org.gradle.caching=true
org.gradle.parallel=true
settings.gradle.kts
rootProject.name = "PROJECT"
include("a", "b", "c")
b/build.gradle.kts
dependencies {
implementation(project(":a"))
}
c/build.gradle.kts
dependencies {
implementation(project(":a"))
}
./gradlew build --stacktrace
scratch.txt
2 runs with 2 different exceptions
Comment From: wilkinsona
Thanks for the sample. Project a is producing a Spring Boot fat jar but projects b and c are configured to depend on project a's normal jar. The jar isn't being built as applying Spring Boot's plugin disables it. You can see this if you just build project b
or c
:
./gradlew clean :c:build --console=plain
> Task :b:clean UP-TO-DATE
> Task :a:clean
> Task :clean
> Task :c:clean
> Task :bootBuildInfo
> Task :a:bootBuildInfo
> Task :a:processResources NO-SOURCE
> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes
> Task :jar SKIPPED
> Task :c:bootBuildInfo
> Task :a:compileJava NO-SOURCE
> Task :a:classes
> Task :c:processResources NO-SOURCE
> Task :a:jar SKIPPED
> Task :c:compileJava NO-SOURCE
> Task :c:classes
> Task :c:bootJar
> Task :c:bootStartScripts
> Task :c:bootDistTar
> Task :c:bootDistZip
> Task :c:jar SKIPPED
> Task :c:startScripts
> Task :c:distTar
> Task :c:distZip
> Task :c:assemble
> Task :c:compileTestJava NO-SOURCE
> Task :c:processTestResources NO-SOURCE
> Task :c:testClasses UP-TO-DATE
> Task :c:test NO-SOURCE
> Task :c:check UP-TO-DATE
> Task :c:build
BUILD SUCCESSFUL in 578ms
14 actionable tasks: 13 executed, 1 up-to-date
Note that project a's jar
task was skipped and that its bootJar
task has not been run. As a result, project c's fat jar does not contain a's jar in BOOT-INF/lib
as there's no file on disk for it to include.
By default the bootJar
task uses the same output location as the jar
task. When you run the whole build in parallel, project b or c may happen to build its fat jar while project a is writing a-1.0.1-SNAPSHOT.jar
. This results in the size changing while the fat jar is being built and an exception being thrown. This happens because the dependencies between the projects are not configured correctly and, therefore, tasks are run in parallel when they should not be.
Packaging one fat jar inside another doesn't really make sense and it's rare for a project with the Spring Boot plugin applied to be used as a dependency. If you do want to use the project as a dependency, you probably want to configure it to produce both a normal jar and a fat jar as described in the documentation. Alternatively, you could stop applying the Spring Boot plugin to the project and use it purely as a library instead.
Comment From: aeiplatform
Thanks, I was hoping at first that the implementation might depend on bootJar by default.
Since the classifier is deprecated in AbstractArchiveTask, here is a fix for build.gradle.kts
tasks {
bootJar.get().archiveClassifier.set("boot")
jar.get().enabled = true
}