Reputation: 149
I recently upgraded Gradle from 4.x to 6.6 for a Spring Boot project. I thought I finally had it all up and running, but then realized that one of our apps, which may start two differently configured tasks of type BootRun
(say A & B), was failing to start the second B instance.
This is the error when I attempt to run the second instance:
Build file 'C:\Users\...\build.gradle' line: 17
Execution failed for task ':apps:myapp:bootRunB'.
> The value for task ':apps:myapp:bootRunB' property 'mainClass' is final and cannot be changed any further.
This is the part of my build.gradle
file that configures the task:
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
doFirst() {
main = bootJar.mainClassName
classpath = bootRun.classpath
systemProperty '...'
}
}
Any suggestions would be appreciated.
Upvotes: 9
Views: 10537
Reputation: 1
Dhruv Tyagi's suggestion works, but the "none" option causes this error. The other two options work for me: enter image description here
Upvotes: 0
Reputation: 812
This issue comes when the configuration is not correct so you need to select the appropriate shorten line command. see below screenshot
Note : In my case user-local default: none is working
Upvotes: 2
Reputation: 71
bootRun task is based on JavaExec task.
It provides now a new attribute that takes a provider. Instead of filling main attribute, fill mainClass attribute:
Sample with kts scripting language:
tasks.register<BootRun>("myBootRunTask") {
dependsOn("assemble")
group = "Application"
val bootJarTask = tasks.getByName<BootJar>("bootJar")
mainClass.set(provider { bootJarTask.mainClassName })
classpath = bootJarTask.classpath
...
}
Upvotes: 0
Reputation: 149
Here is how we fixed it yesterday:
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
mainClass = 'com.App'
doFirst() {
main = bootJar.mainClassName
classpath = bootRun.classpath
systemProperty '...'
}
}
Upvotes: 2
Reputation: 14543
Gradle recently introduced an API for lazy configuration and now internal functionality like plugin extensions and task types are migrated to this new API. In summary, it can be stated that (almost) every configuration property in the build script should be realized using Property<T>
or Provider<T>
(read-only) instead of just a getter and setter of the simple type T
. To provide backward compatibility, a lot of properties are not changed, but new properties are added and the old properties read from and write to these new properties.
This is also the case for your problem, since the task type BootRun
from the Spring Boot Gradle Plugin extends the JavaExec
task type provided by Gradle. The new property mainClass
(a Property<string>
) was added and the old property main
was modified to use the new property. The method getMain()
reads from mainClass
using Property.get()
and the method setMain(String)
, which is invoked when using the syntax main = '...'
in Groovy, writes to mainClass
using Property.set(String)
.
This would all work fine, but Gradle introduced some additional features to their lazy configuration APIs. One of these features is the finalization of writable properties (Property<>
). At some point in the build, a task property (e.g. mainClass
) will be read for its original purpose (running the task), so any changes after that point won't have effect. In earlier versions of Gradle, this caused a lot of difficult to debug problems, because Gradle would not show any error. Now those properties are finalized once they are read for their original purpose, causing Gradle to fail when someone tries to change them afterwards.
Regarding your use case, the property mainClass
(accessed by main
) is already finalized in the doFirst
closure, so you need to apply this configuration earlier. Since you want to set the value to the value of the property bootJar.mainClassName
(which is a simple String
), you must ensure that this property has its final value before it is read for configuring the bootRunB
task:
bootJar {
mainClassName = '...'
}
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
main = bootJar.mainClassName
// From this point, bootRun.classpath must not be changed !
classpath = bootRun.classpath
systemProperty '...'
}
To eliminate this dependency on the configuration order, you may create a Provider<String>
using Project.provider(...)
:
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
main = provider({ bootJar.mainClassName })
classpath = bootRun.classpath
systemProperty '...'
}
Upvotes: 9