Skip to content

Provider and Property APIs implicit task dependency inference does not work when creating instance with injected ProjectLayout #3811

@mkobit

Description

@mkobit

Task inference should work with any provider that is linked from a task (from https://docs.gradle.org/nightly/userguide/lazy_configuration.html#sec:lazy_configuration_roadmap)

I can't share the full code right now, but I can provide a contrived example below. If a "more real" example is needed, I can do that as well.

Expected Behavior

The APIs should be symmetric. It is expected that wiring a Provider<> from one task into the next produces an implicit task dependency. Any task or plugin that wires Provider<> and Property<> types (not just filesystem output types) to other tasks should also get implicit task dependsOn.

Current Behavior

Tasks do not have implicit dependency depending on how the object was created. dependsOn is explicitly required.

Context

In my case, I am writing a plugin that is:

  • Creating an extension object with DirectoryProperty and RegularFileProperty fields tmhat are created from project.layout.buildDirectory.dir and project.layout.directoryProperty
  • Tasks are creating with @Inject with constructor arguments ObjectFactory, ProviderFactory, and ProjectLayout. The properties (like Property<String> and DirectoryProperty) are created from these injected types
  • The task Property<> types are wired into inputs of tasks by way of myTask.inputDir.set(extension.destinationDir)

Steps to Reproduce (for bugs)

This is a basic example without a plugin setting up the inputs and outputs. I can provide a more full one if needed.

  • build.gradle

    import javax.inject.Inject
    wrapper.gradleVersion = '4.4'
    
    // https://docs.gradle.org/nightly/userguide/lazy_configuration.html#sec:lazy_configuration_roadmap example
    class ProducerNewOutputFile extends DefaultTask {
      @OutputFile
      final RegularFileProperty outputFile = newOutputFile()
    
      @TaskAction
      void produce() {
        String message = 'Hello, World!'
        def output = outputFile.get().asFile
        output.text = message
        logger.lifecycle("Wrote '${message}' to ${output}")
      }
    }
    
    class ConsumerNewInputFile extends DefaultTask {
    
      @InputFile
      final RegularFileProperty inputFile = newInputFile()
    
      @TaskAction
      void consume() {
        def input = inputFile.get().asFile
        def message = input.text
        logger.lifecycle("Read '${message}' from ${input}")
      }
    }
    
    task producerNOF(type: ProducerNewOutputFile)
    task consumerNOF(type: ConsumerNewInputFile)
    
    consumerNOF.inputFile = producerNOF.outputFile
    producerNOF.outputFile = layout.buildDirectory.file('producerNOF.txt')
    
    // Injection example
    class ProducerInjectLayout extends DefaultTask {
    
      @Inject
      ProducerInjectLayout(ProjectLayout layout) {
        outputFile = layout.fileProperty()
      }
    
      @OutputFile
      final RegularFileProperty outputFile
    
      @TaskAction
      void produce() {
        String message = 'Hello, World!'
        def output = outputFile.get().asFile
        output.text = message
        logger.lifecycle("Wrote '${message}' to ${output}")
      }
    }
    
    class ConsumerInjectLayout extends DefaultTask {
    
      @Inject
      ConsumerInjectLayout(ProjectLayout layout) {
        inputFile = layout.fileProperty()
      }
    
      @InputFile
      final RegularFileProperty inputFile
    
      @TaskAction
      void consume() {
        def input = inputFile.get().asFile
        def message = input.text
        logger.lifecycle("Read '${message}' from ${input}")
      }
    }
    
    task producerInject(type: ProducerInjectLayout)
    task consumerInject(type: ConsumerInjectLayout)
    
    consumerInject.inputFile = producerInject.outputFile
    producerInject.outputFile = layout.buildDirectory.file('producerInject.txt')
  • Run ./gradlew consumerNOF - see success and task ordering as described in linked documentation

  • Run ./gradlew consumerInject - specified for property 'inputFile' does not exist and no task dependency inference

Your Environment

  • Gradle 4.4

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions