Determine if a task is defined in an external build.gradle file - gradle

I have a gradle task that is created at runtime to call another task ("myOtherTask") which is in a separate gradle file. The problem is if that other task doesn't exist an exception will be thrown. Is it possible to check that a task exists in an external gradle file before attempting to call it?
Example:
task mainTaskBlah(dependsOn: ':setupThings')
task setupThings(){
//...
createMyOtherTask(/*...*/)
//...
}
def createMyOtherTask(projName, appGradleDir) {
def taskName = projName + 'blahTest'
task "$taskName"(type: GradleBuild) {
buildFile = appGradleDir + '/build.gradle'
dir = appGradleDir
tasks = ['myOtherTask']
}
mainTaskBlah.dependsOn "$taskName"
}

You can check if the tasks exists. For example if we wanted to simulate this we could make the task creation triggered by a command line property
apply plugin: "groovy"
group = 'com.jbirdvegas.q41227870'
version = '0.1'
repositories {
jcenter()
}
dependencies {
compile localGroovy()
}
// if user supplied our parameter (superman) then add the task
// simulates if the project has or doesn't have the task
if (project.hasProperty('superman')) {
// create task like normal
project.tasks.create('superman', GradleBuild) {
println "SUPERMAN!!!!"
buildFile = project.projectDir.absolutePath + '/build.gradle'
dir = project.projectDir.absolutePath
tasks = ['myOtherTask']
}
}
// check if the task we are interested in exists on the current project
if (project.tasks.findByName('superman')) {
// task superman exists here we do whatever work we need to do
// when the task is present
def supermanTask = project.tasks.findByName('superman')
project.tasks.findByName('classes').dependsOn supermanTask
} else {
// here we do the work needed if the task is missing
println "Superman not yet added"
}
Then we can see both uses cases rather easily
$ ./gradlew -q build -Psuperman
SUPERMAN!!!!
$ ./gradlew -q build
Superman not yet added

This won't help you find if the task is in a specific external file, but if you just want to determine if a task is defined in any of your imported gradle files...
From gradlew help I see there is a tasks task.
Sadly, gradlew tasks doesn't always show all taks. Some of my projects have an integrationTest task while others do not, in which case I can only go as far as build. However, the default tasks command lists integrationTestClasses but not integrationTest.
From gradlew help --task tasks I can see there is a report expanding --all parameter that we can use.
Now, I can see all tasks via gradlew tasks --all, so a simple grep can tell me whether or not the task I want exists. In bash, this might look like:
TASK="integrationTest"
if gradlew tasks --all | grep -qw "^$TASK"
then
gradlew clean integrationTest
else
gradlew clean build
fi
FYI --
Personally, I needed something to tell me in a git pre-commit hook whether the integrationTest task existed or not, so I know whether I can run gradlew integrationTest or if I have to stop at gradlew build. Not finding an answer here, I kept looking, and this is what I came up with to solve my problem. Hopefully, this is of use to others as well.

I made a little "tool" for stuff like that - maybe it comes in handy for some of you...
$ cat if_gradle_task_exists
#!/bin/sh
TASKS=$(./gradlew tasks --all)
BUILD="./gradlew "
for COMMAND in $#; do
echo "$TASKS" | grep -q "$COMMAND" && BUILD="$BUILD $COMMAND"
done
$BUILD
You can append as many tasks as you like and only the existing ones are executed.
I use it in combination with alias as a kind of super api wrapper for tasks I want to be done in different projects (and I don't want to have to care if any of the specific tasks really do exist):
alias ge='~/.config/bin/if_gradle_task_exists eclipse initDb createTestUsers startLdapServerMock startBrokerMock'
That allows me to be as lazy as ge
stumpf#HV000408:/c/devel/workspace/myproject $> ge
to set up the project for eclipse and prepare all needed servers for local development.
The list of tasks produced will need some time to be set-up, so I wouldn't recommend to use it as a full gradlew wrapper though.
Regards

Related

gradle custom task that depends on build task without testing

I am using gradle 6.0.1
I am trying to write my on task, but I want first the the build task is executed but without tests.
I tried (from build.gradle):
task startEnv(type: GradleBuild) {
tasks = ['build']
doLast {
// START ENV CODE
}
}
However, I don't manage to find a way to call build without running tests, as I would run
gradle build -x test
Is it possible to achieve this functionality?
Another option I can use, is to check inside my startEnv task whether build already exists and run this task only if build exists - Is there a way to query whether build exists? (this is a multi module projects, so I am not sure it is enough to check whether build directory exists on the root project).
I followed the comments and tried the solution mentioned at Skip a task when running another task
I added to build.gradle:
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(startEnv)) {
println("DEBUG1")
test.enabled = false
}
}
task startEnv(type: GradleBuild) {
tasks = ['build']
doLast {
// START ENV CODE
}
}
But when I run ./gradlew startEnv - it still fails with some tests that in current phase I know they should fail.
I can see the DEBUG1 print when I execute this command but the build fails with tests that are failing.
Thank you,

Execute more than one command in a task without breaking incremental build

We use gradle incremental builds and want to run two commands (ideally from one task). One of the solutions here worked getting the two commands running... however it breaks incremental builds.. It looks something like:
task myTask() {
inputs.files(inputFiles)
project.exec {
workingDir web
commandLine('mycmd')
}
project.exec {
workingDir web
commandLine('mysecond-cmd')
}
}
if running a single command and incremental builds is working, the task looked similar to this, the thing that seems to make the difference is the workingDir:
task myTask(type: Exec) {
workingDir myDir // this seems to trigger/enable continuos compilation
commandLine ('myCmd')
}
the best alternative so far is create 3 tasks, one for each of the cmdline tasks I want to run and a third one to group them, which seems dirty.
The question is: Is there a way to run two or more commands in one task with incremental builds still working?
I'll try to answer the question from the comments: how can I signal from a task that has no output files that the build should watch certain files. Unfortunately, this is hard to answer without knowing the exact use case.
To start with, Gradle requires some form of declared output that it can check to see whether a task has run or whether it needs to run. Consider that the task may have failed during the previous run, but the input files haven't changed since then. Should Gradle run the task?
If you have a task that doesn't have any outputs, that means you need to think about why that task should run or not in any given build execution. What's it doing if it's not creating or modifying files or interacting with another part of the system?
Assuming that incremental build is the right solution — it may not be — Gradle does allow you to handle unusual scenarios via TaskOutputs.upToDateWhen(Action). Here's an example that has a straightforward task (gen) that generates a file that acts as an input for a task (runIt) with no outputs:
task gen {
ext.outputDir = "$buildDir/stuff"
outputs.dir outputDir
doLast {
new File(outputDir, "test.txt").text = "Hurray!\n"
}
}
task runIt {
inputs.dir gen
outputs.upToDateWhen { !gen.didWork }
doLast {
println "Running it"
}
}
This specifies that runIt will only run if the input files have changed or the task gen actually did something. In this example, those two scenarios equate to the same thing. If gen runs, then the input files for runIt have changed.
The key thing to understand is that the condition you provide to upToDateWhen() is evaluated during the execution phase of the build, not the configuration phase.
Hope that helps, and if you need any clarification, let me know.

Gradle short task

For fust build project I use such command
gradle clean build -x checkstyleMain -x checkstyleTest -x findbugsMain -x findbugsTest -x test
How I can create short task for this?
Something like this
task short {
clean
// build-x checkstyleMain -x checkstyleTest -x findbugsMain -x findbugsTest -x test
}
I have error with -x
UPDATE
I add such
gradle.taskGraph.whenReady {
if (gradle.taskGraph.hasTask(":fastRun")) {
checkstyleMain.enabled = false
checkstyleTest.enabled = false
findbugsMain = fasle
findbugsTest = false
test = false
}
}
task fastRun {
// clean
// build
}
And run
gradle clean build fastRun
But all tasks run =(
Gradle is not lifecycle based the way Maven is. Instead of asking for a task that includes all these other tasks you do not want to do, you are better off finding a task that does what you want without including all these others.
For example, assuming you are using the java plugin:
assemble: will create all archives in the project, but not run any tests or checks
compileTestJava: will compile all main and test Java classes but will not run tests or create binaries. Unless their creation is required by a different project in a multi-project build.
???: some task that maybe does exactly what you want
And if point 3 has no answer for you, you can define a new task that will depend only on what you want to achieve and not the rest.
See the Java plugin documentation for an exhaustive list of the tasks added, including the high level ones.
Unfortunately, usual ways of skipping tasks won't work in your case just out of the box.
But you can use a TaskGraph to check whether your custom task will be executed and if it'll be, disable all the tasks you don't want to be executed. For that, you need to add such a configuration snippet:
gradle.taskGraph.whenReady {
if (gradle.taskGraph.hasTask(":short")) {
checkstyleMain.enabled = false
checkstyleTest.enabled = false
// any other task you want to skip...
}
}
This snippet should be placed into the root of the build skript. Just note, that task names could differ depending on the project structure you have.
It's waiting until the task graph is ready and if it has a task named short (that means, that this task will be executed), then it disables some other tasks.
You can add the following codes to skip the tasks,
gradle.startParameter.excludedTaskNames += "testClasses"
gradle.startParameter.excludedTaskNames += "test"

Skip a task when running another task

I added a task to my gradle project:
task deploy() {
dependsOn "build"
// excludeTask "test" <-- something like this
doFirst {
// ...
}
}
Now the build task always runs before the deploy task. This is fine because the build task has many steps included. Now I want to explicitly disable one of these included tasks.
Usually I disable it from command line with
gradle deploy -x test
How can I exclude the test task programmatically?
You need to configure tasks graph rather than configure the deploy task itself. Here's the piece of code you need:
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(deploy)) {
test.enabled = false
}
}
WARNING: this will skip the actions defined by the test task, it will NOT skip tasks that test depends on. Thus this is not the same behavior as passing -x test on the command line
I don't know what your deploy task does, but it probably just shouldn't depend on the 'build' task. The 'build' task is a very coarse grained lifecycle task that includes tons of stuff you probably don't want.
Instead it should correctly define its inputs (probably the artifacts that you wanna deploy) and then Gradle will only run the necessary tasks to build those inputs. Then you no longer need any excludes.
I ran into a similar problem. Here is how I prevent "test" from running when I run "intTest" and want the ITs to run alone:
test {
onlyIf { !gradle.startParameter.taskNames.contains("intTest") }
}
An alternative that doesn't depend on certain tasks being run explicitly:
test {
onlyIf { !gradle.taskGraph.hasTask(":intTest") || gradle.taskGraph.hasTask(":check") }
}

Gradle multi project, scope task

I have a multi project in the following way:
rootProject
Subproject1
Subproject2
task whatever << {
println "WHATEVER"
}
I want to be able to configure task 'whatever' once and execute it from any scope (root or subproject) and be executed only once!
This means:
If I run /gradle whatever, I should get: "WHATEVER"
If I run /subproject1/gradle whatever, I should get: "WHATEVER"
In summary, I don't what to execute the same task several tasks according to the number of projects.
I haven't been able to get such a simple result. Please let me know if you can offer any help! Thanks!
gradle whatever searches for whatever in the current subproject and below. Instead, use gradle :whatever and declare the task in the root project.

Resources