What's the right way to declare outputs in a custom task during configuration phase?

Hi… I just upgraded to Gradle 2.2.1 from 1.11. The deprecation warning (output files should not be manipulated after execution of the task begins) that had long mocked me is now really unavoidable.

I have a two part question. The first, I’m not sure what this is protecting me from. I’m sure it’s there for a good reason but if someone could clarify that, it would probably help me understand how to work a better build process,

Secondly, what am I doing wrong if I have properties that are defined during the initialize process so I can’t declare outputs in the configuration of the task, e.g.:

project.task(‘myCustomTask’) {

outputs.file(project.ext[‘notdefinedyet’])

doLast {

// task stuff

}

}

I can’t move the declaration of outputs into the execution part of the task, but in the configuration part of the task the property isn’t defined yet. In my case, this is because the undefined property is being defined as a composite of a base path defined in a plugin configuration block i.e., via the main build script) and EXTra properties that are loaded during initialization.

What is a good practice for doing this right?

Andy

> The first, I’m not sure what this is protecting me from.

This value is read before the task executes for up to date checking. Incremental building can’t work correctly if it changes after this point.

> What is a good practice for doing this right? 

Based on your explanation, I don’t understand why the value is not available until the task starts executing.

Ah, I see, incremental build, thank you.

This is more description of my dilemma:

task init() << {

project.ext.builddir = “c:/temp/1234”

}

task t2(dependsOn:init) {

outputs.files(project.ext.builddir)

doLast{

def nfile = new File(project.ext.builddir,“newfile.txt”)

nfile.mkdirs()

nfile.createNewFile()

println "Created " + nfile.path

}

}

In this case, ‘project.ext.builddir’ is not available but I can’t put the outputs declaration in the executing block. How should I do this when the outputs of one task are dependent on the completion of another task?

Perhaps break this functionality up into two separate tasks. One that generates the output file into a location known at configuration time and a second that copies that file to the final location. This would allow you to continue to leverage incremental build support for the first task. You could even add some logic to the second task to conditionally do the copy based on if ‘task1.didWork’ or something else along those lines.

Hmm, that’s a thought, I’ll need to investigate it further. I may have an issue doing it because there are a slew of dependencies at execution time which contribute to the ability to generate the “output”. It just seems like the interplay of the actual task dependency realized in the execution phase is tangled up with the runnability of the task due to the pseudo-dependencies at the configuration stage. But if I separate them, it seems like I’m basically doing my own up-to-date management which is what I was avoiding prior to my Gradle upgrade.

If your inputs and outputs aren’t known at configuration time then yes, you do effectively have to implement your own conditional build logic. My thought was to separate the tasks so that at least part of it can be known at configuration time, and therefore leverage incremental build support.

A lot of this is being spun from custom plugin configuration - I don’t know ahead of time how the project team will configure, say, output folders but I do know that the output will be in them. So it seems pretty basic but somehow I’m making it hard.

You should be able to have output directories be configurable but still available at configuration time. I’m not sure what your use case is for determining an output folder or file at execution time but in general, any kind of configuration at execution time should be avoided.

Yes, I do agree with that. But can they also be defined as output folders at configuration time if they are, in fact, not actually configured until configuration time? I wonder if the problem is that I’m not using the native ‘gradle.properties’ file, rather, there is a plugin-specific properties file that is loaded during the plugin initialization. Maybe the ‘gradle.properties’ and any ‘-P’ properties would be available in the configuration blocks. I’ll check that. Though it wouldn’t be ideal, that was my intent in the first place was to keep these particular properties out of the ‘gradle.properties’ file so as to not clutter up someone else’s properties.

Depends on the implementation of the plugin but “plugin initialization” is still part of the configuration phase. However, a plugin can additionally add logic run during the execution phase as well (ie. tasks).

Thanks. . . I’m not sure what’s happening yet but the simple case (above) that I thought wasn’t working actually is working . . . So it must be something about the way I’m loading those extra properties. Thanks for your help!