Is it possible to pass a value out of an extension task?

I have a plugin that implements a task:

task foo(type: MyPluginType) {
   input "input"
   output "output"
}

All well and good. I realize that for one set of real-world uses for this task, I want to know more about what the task did. I’d like to be able to do this:

task foo(type: MyPluginType) {
   input "input"
   output "output"

  doLast {
    def returnCode = ???
    if (returnCode == ...) { ... }
  }
}

Where ??? will allow me to query some part of the task state, or represents something that the task passed back.

I can imagine doing this like JavaExec by passing in an open OutputStream, having my extension task write to that stream, using a custom output stream, etc. (Actually, as I write this, it occurs to me that I could pass any kind of object reference, so maybe that will turn out to be the answer).

But before I go off and implement that, am I overlooking the obvious?

I thought I’d see if I could implement passing in an object reference for this case, but I’m also trying to use the WorkQueue to run these tasks and apparently I can’t pass an object through WorkParameters because they’re serialized somewhere along the way?

If I was writing a custom task that I want to react to certain things that the task might encounter, I would likely implement callbacks rather than adding another task action. For this example, the task would need an onComplete method that takes an Action<Integer> (if returnCode is an integer). Usage would be similar to:

task foo(type: MyPluginType) {
    input "input"
    output "output"

    onComplete { returnCode ->
        if (returnCode == ...) { ... }
    }
}

You can be fairly prescriptive or stay very broad with what you offer and can react to with various callbacks. For example, imagine onStatus(43) { ... } where you put the logic in the task to dispatch to the appropriate handler, but only put the exact thing you want to happen when you configure the particular task.

Also, I can’t tell exactly what you have (or intend to have), but the callback should be workable with WorkQueue as well.

That’s an interesting idea. I confess I’m still relatively new to Gradle/Groovy programming. I thought I’d got my head around the major concepts, but I’m struggling to see how this works.

A pointer to an example would be great, but in the meantime, I’ll see if I can work out what an Action<Integer> would look like and how I’d call it from my plugin.

I attempted to pass the Action<Integer> callback through WorkParameters, but just like my object reference, attempting to put it on the work queue raises a could not serialize error:

* What went wrong:
Execution failed for task ':valid_output_rc'.
> A failure occurred while executing com.nwalsh.gradle.relaxng.validate.RelaxNGValidate
   > Could not isolate value com.nwalsh.gradle.relaxng.validate.RelaxNGValidateWorkParameters_Decorated@778575e3 of type RelaxNGValidateWorkParameters
      > Could not serialize value of type ConfigureUtil.WrappedConfigureAction

The gradle-docker-plugin allows callbacks in many of the tasks. The handlers are being called in the task action:

Thanks. I’ll dig in and see if I can get my head around it. FWIW, my plugins are at GitHub - ndw/relaxng-gradle: Gradle plugins to perform RELAX NG validation and translation (wrappers for Jing and Trang, basically)

The design committed at the moment allows an errorHandler to be passed in and simply doesn’t try to use the WorkQueue if that option has been passed.

I think that there’s some recent design changes that are directly impacting that with the WorkQueue, but it may just be that a Closure that was converted to an Action never would have worked, even if a Closure could. Considering that an Action is preferred over a Groovy Closure for use with other JVM languages, either way, it might be something worth trying to get addressed in Gradle itself.

That is well beyond my ability, but I’ll keep plugging along as best I can.

Oh yeah, definitely didn’t mean for you to try to fix it. It just looks like it might be something that could/should work based on less preferred options that should work based on what I see (but without trying it). If it works when using a non-recommended method, there’s incentive to make the recommended method work too.