Setting TaskInputs/TaskOutputs conditionally

Normally I would do something like this in a task class

@InputFile
File getMyInput() { ... }

or

@Input
String getMyInput() { ... }

The above is pretty easy if you know your input type, but I am looking at a case where I am not sure what it is going to be. However I still want to reap the benefits of it being a file/folder when it is, and when it is not then I still want caching to occur in order to prevent unnecessary task execution.

I reckon that at some arbitrary point in code, which I have not determined as yet, I could do something like

switch(this.myInput) {
    case File:
      inputs.file this.myInput
      break
        default:
      inputs.property 'myInput' : this.myInput
 }

(OK, the code is pretty simplified, but that’s the idea).

My question is where do I hook this code in. Using ‘afterEvaluate’ would not be the right thing to do as I cannot control the order of actions being executed for ‘afterEvaluate’. My feeling is that this should happen later on, just before gradle actually reads in all the TaskInputs/TaskOutpus to check up-to-date status.

Any suggestions?

It’s hard to tell from this limited example but I think the new ‘TextResource’ mechanism might be a good fit. Alternatively, couldn’t you just define two different ‘@Optional’ inputs and configure whichever was appropriate for the given use case?

Alternatively, couldn’t you just define two different @Optional inputs and configure whichever was appropriate for the given use case?

Already thought about that - it might be an option.

It’s hard to tell from this limited example

Consider that the resources might be local files or files/directories on remote servers accssed via ftp/http/sftp etc.

I think the new TextResource mechanism might be a good fit.

The plugin needs to be compatible with Gradle 2.0, so out of scope at present. However, you have given me a great idea for a future implementation.

Going back to your code example above, couldn’t you put that in some kind of setter method?

public void input(Object input) {

// code to set inputs based on type

}

If one allows settings the inputs within a setter, then allowance must be made for clearing them as well.

Consider the following code snippet from a ‘Whatever’ Task class:

void myInput(Object... inp) {
  inp.each {
    this.myInputs.add(it)
    switch(it) {
      case File:
       inputs.file it
       break
     // snip.. let's ignore rest of code for now
    }
  }
}
  void setMyInputs(Object... inp) {
  this.myInputs.clear() // <-- missing removal from task.inputs
  myInputs(inp)
}
  private List<Object> myInputs

Then in the gradle script

task example( type : Whatever ) {
  myInput file('x')
  setMyInput file('y')
}

Somehow ‘x’ now has to be removed from ‘example.inputs’.

Yes, that becomes problematic. This is the fundamental limitation of the exiting implementation of the configuration phase, being that it becomes difficult to determine evaluation ordering. The new configuration model is intended to address this, but right now I think your only option is the multiple input properties. I see of no other way to ensure that your inputs are dynamically resolved before your task executes. In fact, it is more complex than that, as your inputs have to be defined before Gradle attempt to determine whether your ‘@TaskAction’ needs to execute, and I know of no public hook to effectively determine when your task is “finished” being configured other than ‘Project.afterEvaluate()’. Like you said, there is no guarantee that further configuration is not done in subsequent ‘afterEvaluate’ blocks.