Script task dependency not working anymore with gradle 6.4+

After updating from gradle 6.3 to 6.4 and above script task outputs used as dependency don’t seem to work anymore.

To order my build logic I implemented some basic tasks in a gradle file gradle/tasks.gradle. If I implement the defined task in a sub-project and use it’s outputs as dependency in another sub-project, gradle 6.4 and above will not add proper dependencies between the corresponding tasks.

This is an MVCE to illustrate the problem:

gradle/tasks.gradle:

import java.time.LocalDateTime

class MvceTask extends DefaultTask {

  @OutputFile
  RegularFileProperty outputFile = project.objects.fileProperty()

  @TaskAction
  void execute() {
    def tOutFile = outputFile.get().asFile
    tOutFile.parentFile.mkdirs()
    tOutFile.text = "MvceTask was here at " + LocalDateTime.now()
  }
}
rootProject.ext.MvceTask = MvceTask

project1/build.gradle:

apply from: "${project.rootDir}/gradle/tasks.gradle"

configurations {
  dist
}

task buildProject1(type: MvceTask) {
  outputs.upToDateWhen { false }
  outputFile = rootProject.file("${buildDir}/mvce.txt")
}

artifacts {
  dist buildProject1.outputFile
}

project2/build.gradle:

configurations {
  buildSource
}

dependencies {
  buildSource project(path: ':project1', configuration: 'dist')
}

task buildProject2(type: Copy) {
   from configurations.buildSource
   into rootProject.file("${buildDir}")
}

As said above with gradle 6.3 this works as desired:

mvce>gradlew buildProject2 --dry-run
:project1:buildProject1 SKIPPED
:project2:buildProject2 SKIPPED

BUILD SUCCESSFUL in 754ms

But with gradle 6.4 and above the dependency to project1:buildProject1 is ignored:

mvce>gradlew buildProject2 --dry-run
:project2:buildProject2 SKIPPED

BUILD SUCCESSFUL in 732ms

There are no hints on a breaking change with regard to this scenario in the 6.4 release notes. The only thing that looks like it might be related is the Precompiled Groovy DSL script plugins topic. But there are no migration steps mentioned.

Is this a known regression and how could I achieve the same behavior as in gradle 6.3?

Hi,

Because MvceTask is not an AbstractArchiveTask, you need to tell Gradle how it must build the output file:

project1/build.gradle:

artifacts {
    dist(buildProject1.outputFile) {
        builtBy buildProject1
    }
}

term:

/tmp$ ./gradlew --version && ./gradlew buildProject2 --dry-run

------------------------------------------------------------
Gradle 6.4
------------------------------------------------------------
[...]

:project1:buildProject1 SKIPPED
:project2:buildProject2 SKIPPED

BUILD SUCCESSFUL in 2s

Edit, sourcing the resolution:

Here the “artifact” we’re attaching is a task that actually generates a Jar. Doing so, Gradle can automatically track dependencies of this task and build them as needed. This is possible because the Jar task extends AbstractArchiveTask . If it’s not the case, you will need to explicitly declare how the artifact is generated.
in Sharing outputs between projects

Thanks for the reply.

As outputFile of MvceTask is a RegularFileProperty gradle should be able to pick up the information, that the buildProject1 task creates this file. Actually it was able to get this right in gradle 6.3. Thus I’m wondering why this broke without notice (or no notice I was able to find) and if this was on purpose.

I just found, that the documentation mentions my expectation as supported behavior. That is, this very likely is a bug.

From ArtifactHandler - Gradle DSL Version 8.4 :

A Provider of File , RegularFile or Directory . The information for publishing the artifact is extracted from the file or directory name. When the provider represents an output of a particular task, that task will be executed if the artifact is required.

You can however skip the builtBy and have an automatic resolution of the task when using the configuration avoidance API:

project1/build.gradle:

def bp = tasks.register('buildProject1', MvceTask) {
  outputs.upToDateWhen { false }
  outputFile = rootProject.file("${buildDir}/mvce.txt")
}

artifacts {
  dist(bp.map { it.outputs.files.singleFile })
}

With the legacy API, I was not able to restore Gradle 6.3 behavior.

I remember that many things changed when variants where introduced in Gradle, and there could have been side effects. And this looks like it is a side effect of their introduction/maturation. Whether this is desired or a regression though, I cannot say. You could try to open an issue to get an answer from the staff.

I implemented the builtBy workaround above and filed Task not executed if `RegularFileProperty` task output is used as artifact (gradle 6.4+) · Issue #15792 · gradle/gradle · GitHub

Thanks for your input.