Auto-create task dependecies in case of computed @OutputFile Provider<RegularFile>

Hi,

I’m trying to get task dependencies auto-created in a case that Producer task computes output file based on other @Input.

Below is an example based on Lazy Configuration doc.

Without dependsOn(producer), gradlew producer execution fails with:

Type 'Consumer' property 'inputFile' specifies file '<some-dir>build/file2.txt' which doesn't exist.

My constrain is that I need to preserve this input outputFileName in the Producer task.

I would be grateful for suggestions how to get it working and why it may not be working.

Many thanks,

abstract class Producer extends DefaultTask {

    @Input
    abstract Property<String> getOutputFileName()

    @OutputFile
    Provider<RegularFile> outputFile = getOutputFileName().flatMap(name -> project.layout.buildDirectory.file(name))

    @TaskAction
    void produce() {
        String message = 'Hello, World!'
        def output = outputFile.get().asFile
        output.text = message
        logger.quiet("Wrote '${message}' to ${output}")
    }
}

abstract class Consumer extends DefaultTask {
    @InputFile
    abstract RegularFileProperty getInputFile()

    @TaskAction
    void consume() {
        def input = inputFile.get().asFile
        def message = input.text
        logger.quiet("Read '${message}' from ${input}")
    }
}

def producer = tasks.register("producer", Producer) {
    outputFileName = 'file2.txt'
}

def consumer = tasks.register("consumer", Consumer) {
    inputFile = producer.flatMap { it.outputFile }
//    dependsOn(producer)
}

For example either like this:

@OutputFile
abstract RegularFileProperty getOutputFile()

Producer() {
    outputFile.value(getOutputFileName().flatMap(name -> project.layout.buildDirectory.file(name))).disallowChanges()
}

or if the type must really be Provider<RegularFile>, for example

@OutputFile
protected abstract RegularFileProperty getOutputFileProperty()

@Internal
Provider<RegularFile> outputFile = outputFileProperty

Producer() {
    outputFile.value(getOutputFileName().flatMap(name -> project.layout.buildDirectory.file(name))).disallowChanges()
}

Thank you,

Both seems to work.

The intention to have a Provider<RegularFile> is not to have a modifiable property that is accessible for a task consumer (these tasks are part of a plugin).

As far as I checked, in both examples it is possible to attempt to set that property and both will fail due
to disallowChanges not due to that the the fact that there is no property to modify.

But this might just do :slight_smile: , unless anything else comes to your mind.

Being a curious beast and trying to better understand how lazy-configuration works, what is the general reason why my initial attemp was not working ? (unless this is too complex to explain failry briefly).

I’m not fully sure.
Afair, the implicit dependencies only work on Propertys, not on Providers.
Whether this is intended, or a missing feature I don’t know.
For example this works:

@OutputFile
final RegularFileProperty outputFile = objects.fileProperty()

while this does not:

@OutputFile
final Provider<RegularFile> outputFile = objects.fileProperty()

This also works:

@OutputFile
final RegularFileProperty outputFile = objects.fileProperty().value(getOutputFileName().flatMap(name -> project.layout.buildDirectory.file(name)))

while the same with Provider<RegularFile> does not work.
As well as the following which is also not working:

@OutputFile
final RegularFileProperty outputFile = objects.fileProperty().value(getOutputFileName().flatMap(name -> project.layout.buildDirectory.file(name))).disallowChanges()

Maybe this get changed if someone posts a feature request, I don’t know.
Or maybe you could at least request a more in-depth documentation on what is necessary that it works.

With the solution that splits outputFile provider and property, the consumer should not actually use the property anyway, as it is protected, or you can also make it package-private.
The problem is, that with a Groovy consumer, he can still access it as you can even access private properties with Groovy without further ado.

1 Like

Thank you for your insight

1 Like