Trying to add task dependency but it claims the task doesn't exist

I am trying to generate code with an OpenAPI plugin, the plugin adds a task called processSpring. For some reason gradle believes that tasks depends on generateEffectiveLombokConfig.

Reason: Task ':foo:processSpring' uses this output of task ':foo:generateEffectiveLombokConfig' without > declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending > on what order the tasks are executed.

   > Possible solutions:
     > 1. Declare task ':foo:generateEffectiveLombokConfig' as an input of ':foo:processSpring'.
      > 2. Declare an explicit dependency on ':foo:generateEffectiveLombokConfig' from ':foo:processSpring' using Task#dependsOn.
      > 3. Declare an explicit dependency on ':foo:generateEffectiveLombokConfig' from ':foo:processSpring' using Task#mustRunAfter.

In an attempt to appease Gradle I add:

 tasks.named('processSpring') { task ->
    task.dependsOn('generateEffectiveLombokConfig')
}

But then I get the error:

A problem occurred evaluating project ':foo'.
Task with name 'processSpring' not found in project ':foo'.

How can processSpring not exist in the project when the first error specifically telling me there is a processSpring task in the project that is depending on generateEffectiveLombokConfig?

And not only that but I have this line in the build file before this:

compileJava.dependsOn ('processSpring')

and it doesn’t complain about that at all.

Also the output of ./gradlew tasks -all shows the processSpring task:

Openapi processor tasks
foo:processSpring - process openapi with openapi-processor-spring

For some reason gradle believes that tasks depends on generateEffectiveLombokConfig.

The “some reason” is, that the inputs of processSpring contain any of the outputs of generateEffectiveLombokConfig wihtout the tasks having any declared relation.

But the advices you get from Gradle are imho really bad.
They are just symptom treatment.
They do not fix the real problem and just move the problem aside to a later time where it is even harder to reproduce find and fix.
You should never have any explicit dependsOn (except with a lifecycle task on the left-hand side), but instead wire task outputs to task inputs properly so that you get the necessary task dependency automatically.
How the correct solution is in your case is hard to tell without having a look at the actual build and how it is configured.

How can processSpring not exist in the project when the first error specifically telling me there is a processSpring task in the project that is depending on generateEffectiveLombokConfig?

What it tells you is, that the task does not exist yet when you call the named method. It is added in a later stage. If you really want to go the bad way of adding an explicit dependency, use something like tasks.named { it == "processSpring" }.configureEach { dependsOn(...) }, this will also match tasks that are added after you called it.

and it doesn’t complain about that at all.

Because you do not depend on a task, you depend on a String representation of a task, and this String is resolved to the actual task very late when the task is finally there already.

Unfortunately the two tasks are in different 3rd party plugins and as far as I can tell I have no control over how the task inputs are declared.

After messing with it more after making this post I finally decided it was a race condition and the tasks were being added later and weren’t there when that part of the configuration phase was running. So it is nice to have confirmation that I was on the right track with that.

Unless I am missing something and there is a way for me to declare task inputs for a task in a 3rd party plugin it looks like I will just have to do this workaround. Thanks for the workaround, I will give it a go.

Unfortunately the two tasks are in different 3rd party plugins and as far as I can tell I have no control over how the task inputs are declared.

You usually can configure any 3rd party plugin’s task however you need it.
But as I said, I know too little about your build to tell concretely what to change.

I had the a similar experience. In my case I needed to disable tasks introduced by plugins and all DataBaseUpgradTasks (There is easier ways to disable all tasks of certain type.)

I found something on stackexchange that I can’t find now. But I have the solution. It is about creating a listener that waits for all projects to be configured before calling a closure. I’ll share a snippet from my build.gradle file. The snippet can be used to introduce task dependencies instead of disable them.

static void whenAllProjectsEvaluated(Project rootProject, Closure action) {
    final java.util.concurrent.atomic.AtomicInteger counter = new java.util.concurrent.atomic.AtomicInteger()
    final int allProjectsCount = rootProject.getAllprojects().size()
    rootProject.allprojects { p ->
        p.afterEvaluate {
            final int currentEvaluatedCount = counter.incrementAndGet()
            rootProject.logger.lifecycle("${p.name} - afterEvaluate (${currentEvaluatedCount}/$allProjectsCount")
            if (currentEvaluatedCount == allProjectsCount) {
                action.call()
            }
        }
    }
}
whenAllProjectsEvaluated(project) {
    project['updateAndConfigure'].enabled = false
    project['upgradeConfigFiles'].enabled = false
    project['copyDBFolder'].enabled = false

    project.subprojects.each{
        it.tasks.each{
            if (it instanceof com.iipax.gradle.tasks.DatabaseUpgradeTask)
                it.enabled = false
        }
   }
}

That is stacking up various highly discouraged bad-practices on top of each other.
You should really not do something like that.
Especially when there are so much easier and more reliable ways like tasks.withType<...>.configureEach { ... } and tasks.named { ... }.configureEach { ... } if you really do such manipulation as already said above. :wink:

Might be, but when you can’t get hold of tasks before all projects are evaluated. Then you need to to something that works.

tasks.withType and tasks.named { ... } work on all tasks, be they registered before your code runs or after.

Maybe this is a gradle version issue and tasks.named() works better in newer versions. named() isn’t even available on the version I was forced to use. getByName says that the task isn’t available either. But the solution that you say is full of bad practice works.

I was on 4.8 :blush:

Yes, tasks.named { ... } is relatively new.
tasks.named(...) does not work like that and requires that the task is already there.
But on older versions you can use tasks.matching { it.name == ... }.configureEach { ... }.