Reputation: 8031
I have a build.gradle
file that creates a java WAR
file. This file is used in one stage of a Docker multi stage build to generate a Docker Image that I use in production, staging, etc... (the configuration files / secrets are outside the image).
But in development, although most of the time I use the normal build to generate a WAR file (or an exploded WAR), and it works fine, sometimes I want to just execute a single java file with a main class in my project.
I achieved that adding the following to the file:
task execFile(type: JavaExec) {
main = mainClass
classpath = sourceSets.main.runtimeClasspath
if (System.getProperty('debug', 'false') == 'true') {
jvmArgs "-Xdebug", "-agentlib:jdwp=transport=dt_socket,address=8788,server=y,suspend=y"
}
systemProperties System.getProperties()
}
Then I'm able to execute the file passing system properties with:
gradle execFile -PmainClass=com.mysite.MyClass -Dsomeprop=somevalue
And if I want to debug i run:
gradle execFile -PmainClass=com.mysite.MyClass -Dsomeprop=somevalue -Ddebug=true
This way I can execute (and debug) single files, which is great in some cases during development, but the problem is that this code is executed even if I dont run execFile
explicitly.
This causes errors when building and generating the WAR file, so what I'm doing is commenting these lines, uncommenting when I run single files, and commenting again (when I remember) after I run the files, before pushing the git repo (otherwise the docker build process ends up with errors).
This is very bad, I know.
Based on what I understand, that code is run during the configuration phase, and to make the code run in the execution of the task, I could include the code inside a doLast
method (or use the shorthand <<
):
Why does gradle run every task in gradle.build
But in this case I get the error No main class specified
, probably due to the same reasons stated in this discussion:
https://discuss.gradle.org/t/javexec-error-no-main-class-specified/12731
The JavaExec task has a task action which executes the Java program. By using <<, you’re adding the configuration of the main, classpath, and args as a task action as well. When the task action provided by the JavaExec runs, the second task action to configure these values has not run yet. You likely want to configure these values in the configuration phase, not in a task action by removing the <<.
The error doesn't happen if I remove the doLast
method, but the initial problem would remain unsolved.
So what I want to know is if there is a way (and how) to make what's inside the execFile
task run only when this task is called explicitly.
(so that it won't cause any side effects when other tasks are run)
Upvotes: 6
Views: 5583
Reputation: 455
You can solve this problem with a wrapping task, by creating at runtime the desired JavaExec one inside its doLast { }
closure.
For Groovy DSL:
task myTask {
group = 'MyGroup'
description = 'Runs Hello.java'
dependsOn 'build'
doLast {
tasks.create('myTaskExec', JavaExec) {
main = 'com.example.Hello'
args = ['foo', 'bar']
classpath = sourceSets.main.runtimeClasspath
}.exec()
}
}
For Kotlin DSL:
tasks.register("myTask") {
group = "MyGroup"
description = "Runs Hello.java"
dependsOn(mutableListOf("build"))
doLast {
tasks.create<JavaExec>("myTaskExec") {
main = "com.example.Hello"
args = mutableListOf("foo", "bar")
classpath = sourceSets.main.get().runtimeClasspath
}.exec()
}
}
Note that in both examples the dependencies to another task should be declared in the wrapping task and not in the JavaExec one.
With this change your task should be running only when called:
task execFile {
dependsOn 'build'
doLast {
tasks.create('execFileJavaExec', JavaExec) {
main = mainClass
classpath = sourceSets.main.runtimeClasspath
if (System.getProperty('debug', 'false') == 'true') {
jvmArgs "-Xdebug", "-agentlib:jdwp=transport=dt_socket,address=8788,server=y,suspend=y"
}
systemProperties System.getProperties()
}.exec()
}
}
Upvotes: 8