Gradle: Making JavaExec tasks incremental whilst debugging - gradle

I am working on a gradle project where I have a JavaExec task which runs as part of the build. When running the task normally it is incremental, i.e when the inputs/outputs haven't changed the task is skipped. As the project is built with gradle, when I run my application it builds the required modules before launching.
This works fine when using the standard 'Run' (I am using IntelliJ as my IDE), however if I launch it using 'Debug' (with the same run configuration), the JavaExec task always rebuilds, as the randomly assigned 'address' property in the jvmArgs has changed, despite no other changes to the inputs/outputs.
This turns the usual ~5s launch time (when all tasks are up-to-date) into 1min+, as it reruns the task.
Is there any way to force the address of the jvm that the child process uses, in order to retain the benefit of the incremental builds?
I have tried:
debugOptions closure, eg:
debugOptions {
enabled = true
port = 12995
server = true
suspend = false
}
Explicitly settings the jvmArgs, eg:
jvmArgs '-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=12995'

Related

Configure gradle plugin based on future tasks

I have a plugin where I need to toggle a property base on whether a task is going to run or not. This is needed because when running in ide (intellij) this flag needs to be disabled whereas if the specific runtime task is run this flag needs to be enabled.
I've tried
gradle.taskGraph.whenReady { if (it.hasTask(tasks.runtime)) {
javafx.configuration = "compileOnly"
}
}
but this gives me a error
Cannot change dependencies of dependency configuration ':implementation' after it has been included in dependency resolution.
Is there any way to set this property earlier (during plugin configuration) based on tasks or a better way to complete this?
In build script you can evaluate following properties which IDE adds:
idea.active property that IDE sets when you run Gradle tasks from IDE;
idea.sync.active property which is added by IDE when IDE reloads project from Gradle build scripts.
For example: System.getProperty('idea.active').

Silently handle Gradle task failure

I have a project which should run a specific Gradle task (sonarqube task) only if a condition is met after build task is executed. This task can fail as it communicates with a remote server which is sometimes not available. If the server is unavailable, I'd like to silently handle the error an print some message to console instead of failing the whole build.
Currently I am able to do so with the following configuration:
build.doLast {
if ('desired.value' == System.properties['some.prop']) {
try {
tasks.sonarqube.execute()
} catch(e) {
...
}
}
}
However using this method I get deprecation messages. After reading up a bit I supposedly should not be using execute, so instead I came up with this:
if ('desired.value' == System.properties['some.property']) {
build.finalizedBy sonarqube
}
However in this case, if sonarqube task fails my whole build will fail as well. How could I handle sonarqube task failures in such case?
I am using Gradle 4.5.1.
Edit
The builds are ran using the following command:
./gradlew build
Modifying it like the following will cause Gradle to ignore not only sonarqube failures, but also build failures which is not what I want:
./gradlew build --continue
As you've discovered, once a Gradle task fails it's not possible to elegantly recover from that. Therefore you need to avoid that happening.
I can think of three ways to do that:
Find a way of executing the code you want to execute directly without it being wrapped by a Gradle task (eg via a command line or Java process). Then execute that code inside of a try-catch block in the normal fashion.
Activate an option in the sonarqube task which means that task does not fail when there is a problem but reports back to a parent task in another way (eg by setting a property on the Task object), which can in turn deal with the problem. (Having looked quickly it does not appear any such option exists, but you could request it or write a pull request.)
Run another task before the sonarqube task to check if the remote server is available first (and/or other potential failure conditions); then you can deal with the server not being available gracefully, including not running the sonarqube task.
The last option could work as follows by executing a new sonarqubeWrapper task:
tasks.register("sonarqubeWrapper") {
doFirst {
if (isServerAvailable()) {
// Deal with the server not being available
throw new StopExecutionException()
}
}
doLast('sonarqube')
}
def isServerAvailable() {
// Determine if server available
}

Conditionally ordering tasks in Gradle

Consider a Gradle plugin that adds three tasks to a project - a buildZip task to create a distributable zip of the project, a publishZip task to publish that zip to a shared repository, and a cleanZip task to clean up any local version of the zip. For local development, cleanZip buildZip will be used frequently, but the automated build system will be running buildZip publishZip cleanZip.
One of the projects in which this plugin is being used wants to run their build using Gradle's parallel flag to allow the different parts of the project to be built in parallel. Unfortunately, this runs into a problem with the zip tasks - buildZip depends on the project actually building, but cleanZip doesn't have any dependencies so it can run right away, leading to the automated build system not being able to clean up.
Declaring any dependencies between these tasks isn't a good idea because they should be able to be run separately. Also, I can't specify mustRunAfter (at least between buildZip and cleanZip) because sometimes clean should be first and sometimes build should be first.
How can I tell Gradle what order to run these tasks in, in a way that will be honored by --parallel and isn't hardcoded to have a particular one always run before the other?
What you can do is: detect if gradle is run with --parallel and based on this configure dependencies between tasks appropriately. It can be done in the following way:
println project.gradle.startParameter.parallelProjectExecutionEnabled

Run all microservices in a multi-project gradle build

I have a multi-project gradle build that's roughly set up like this:
RootProject
- ServiceA
- ServiceB
- ServiceC
- UI
Each of these subprojects is using the Spark framework and runs an embedded web server. It's basically a microservices setup, so they all need to be up and running for the system as a whole to work.
They each have a task defined like this:
task runApp(type: JavaExec) {
main = 'App'
classpath = sourceSets.main.runtimeClasspath
}
I know I can manually start each service either through my IDE or by opening a different terminal for each sub-project and running gradlew ServiceA:runApp, but what I'd like to do is create a runSystem task at the root level that will fire up everything to make it easy to run the whole system during development.
How can I do this with Gradle?
If you run a task on the root project, Gradle invokes the same task (if it exists) on all the subprojects. Just execute runApp from the root folder.
In your case however, your runApp task might not exit because it starts a server, so execution will not move on to the next project. You can either enable parallel execution, or modify your tasks to run your server tasks in the background (using ProcessBuilder)

How do I wrap a gradle task in another task?

I am trying to configure two different gradle test tasks that essentially just set some values and then run the built-in test task. I have read the gradle documentation and have been searching for a few hours with no luck. I'm guessing I just don't know how to word this question properly to find anything.
The scenario is that we have selenium tests that we might want to run locally or remotely. If we run them locally we want to configure how many threads it uses, and if we run them remotely we want to set a much higher number of threads and also a system property so that the test runner knows to run remotely.
Essentially here's what I'd like to do:
task testLocal {
maxParallelForks = 2
// now run the built-in test task
}
task testRemote {
maxParallelForks = 4
systemProperties "geb.env": "winxp-firefox"
// now run the built-in test task
}
Ideally I'd also like to be able to pass all the same arguments that the test task supports on the command line, like:
gradle testLocal --tests com.domain.tests.package*
What is the proper way to handle this scenario with gradle?
The proper way to handle this is to have two Test tasks. You'd typically use (and configure) the Java plugin's test task for the local testing, and additionally declare and configure a second testRemote task (task testRemote(type: Test) { ... }). (There is no way to "wrap" a task, or "call" a task from another task.)

Resources