Getting a list of all Gradle tasks from build script - gradle

So I'm trying to write a list of all gradle tasks to a file. I of course could use the tasks command for this, but I want cache it to a file every time any other gradle command is called. So whenever I run ./gradlew build for example, I want the available tasks to be written to a file.
This seemed simple enough, and I wrote the below task to try it out:
task cacheTasks() {
doLast {
allprojects { p ->
p.tasks*.each { t ->
println(p.name + ":" + t.name)
}
}
}
}
The problem is, that I only get a sub-set of all the tasks available. When I run the ./gradlew tasks --all command, many more are printed. It seems that none of the built-in tasks (like build, clean or help) are in the tasks* List when I loop over it, but oddly enough I can reference them directly:
tasks.build { t ->
println("DEBUG:" + t.name)
}
It seems so simple, yet I've been searching in vain for a solution. I even tried looking in the gradle source code to see how the tasks Task works, but I couldn't find any clue as to why this doesn't work.

rootProject.getAllTasks(true) looks like it's retrieving more tasks than rootProject.tasks.

I highly doubt there is a task class in container with name build.
This is what I get when I debug your task:
I am not saying gradle build does not run, but it can be in other forms maybe an instance of org.gradle.api.tasks.GradleBuild. (I am not very sure because the gradle source code is very hard for me to compile and run).
When using
tasks.build { t ->
println("DEBUG:" + t.name)
}
You actually call org.gradle.api.tasks.TaskContainer#create(java.util.Map<java.lang.String,?>, groovy.lang.Closure) and create a new task named build.

Related

How to run a task before build in 2021 gradle Kotlin syntax in an Android project

I read a lot of answers to multiple questions like this, but they are all very old and use arcane/obsolete syntax in groovy and/or are not suitable for Android projects.
I have a task.
tasks.register("asd") {
doFirst {
exec {
How do I run it when build starts in an Android app/build.gradle.kts file?
I have printed both tasks and project.tasks names and I have tried
tasks.named("build").dependsOn(":asd")
tasks.named("app:build").dependsOn(":asd")
tasks.named(":app:build").dependsOn(":asd")
project.tasks.named("build").dependsOn(":asd")
project.tasks.named("app:build").dependsOn(":asd")
project.tasks.named(":app:build").dependsOn(":asd")
It either fails with Task <name> not found in project or it does nothing.
I tried with doFirst, doLast and neither (directly exec) and still nothing.
So I found the right way to go about it
android {
project.tasks.preBuild.dependsOn("asd")
}
tasks.register("asd") {
doFirst {
exec {
No need for the : when referring to the task.
The build and preBuild tasks are accessible from (project.)tasks.
doFirst was necessary otherwise it would loop endlessly.
tasks.register allowed the task to run when it needed and not immediately, which happens if we use create

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 - Error Copying Files To Project Root

I’m getting the following error whenever I attempt to use a Copy task to copy a file into the root of a project (the same folder I’m running gradle from):
Failed to create MD5 hash for file content.
I thought this was related to the artifacts I was pulling from Artifactory, but that seems to be unrelated. I was able to get the same results with a minimal script.
Is there something obviously wrong with what I’m doing, or does Gradle intentionally disallow such things?
task fails(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath
}
task works(type:Copy) {
from 'build/someFile.txt'
into new File('.').absolutePath + '/output'
}
Short Answer: Don't copy into the project directory, you are best to use into "$buildDir/someFolder" so that the folder is isolated to this single task, and also so that it will be cleaned by gradle clean
Long Answer: At it's core, Gradle has the concept of an "UP-TO-DATE" check for every single task. If Gradle sees that nothing has changed since last time a task was executed it will use the old result instead of executing again.
UP-TO-DATE checking is implemented by taking a "hash" of the task inputs and task outputs. Since you are using into '.' that means that the entire contents of the project directory is considered a task output (bad)
Gradle uses the .gradle folder for temp files (eg task hashes) It's likely some of these files are locked for writing as Gradle is trying to also read the same files (to calculate the "hash" of the task outputs) causing the error you are seeing
* EDIT *
If you need to copy into the project directory for legacy reasons, you might use Project.copy(...) directly instead of a Copy task. You could manually manage the task inputs/outputs in this case
Eg
task customCopy {
inputs.file "$buildDir/someFile.txt"
outputs.file 'someFile.txt'
doLast {
copy {
from "$buildDir/someFile.txt"
into '.'
}
}
}
Can you believe it, the following works
task myCopy(type: Copy) {
from "$rootDir/app1/src/main/resources/db"
into "$rootDir/app2/src/test/resources/db"
}
test.dependsOn myCopy
and the following doesn't 🤦
task myCopy(type: Copy) {
from '$rootDir/app1/src/main/resources'
into '$rootDir/app2/src/test/resources'
}
test.dependsOn myCopy

How do I intercept failures of a Gradle task?

'I'm writing build script using net.foragerr.jmeter plug-in, version 1.0.2-2.13.
What my task does is mostly 1) runs plug-in JMeter task and 2) collects application log.
task perfTest(dependsOn: ['jmClean', 'jmRun'],
description:'Runs (cleanly) performance tests on a deployed application and collects the app log if available. ' +
'Use --no-daemon to see progress. Use --info to see all JMeter command-line arguments.') << {
if (logDir.isDirectory()) {
copy {
from "${logDir}"
into "${buildDir}/jmeter-report"
include 'iRePORT.log'
}
}
}
Now, I need to collect the log even if jmRun fails. When I try to implement a solution for this, I am really stuck:
I can't modify jmRun task, because it's a plug-in task
I can't use try/finally and execute another task directly because Gradle is designed not to support this, e.g. see How do I wrap a gradle task in another task?
I can't use --continue flag gradle.startParameter.continueOnFailure = true because it would not continue execution of a dependent task
The only possible workaround that I could think of is to separate log collection, always include it on a command line, e.g. perfTest collectLogs and set gradle.startParameter.continueOnFailure = true in perfTest.
This is far from ideal.
Are there any better solutions? Shouldn't there be a way for Gradle to support scenarios like this?
You could try creating a task like this
task collectPerfTestLogs(type: Copy) {
from "${logDir}"
into "${buildDir}/jmeter-report"
include 'iRePORT.log'
}
and then have perfTest.finalizedBy(collectPerfTestLogs).
To quote Gradle:
Finalizer tasks will be executed even if the finalized task fails.
See https://docs.gradle.org/current/userguide/more_about_tasks.html

Resources