Dependencies custom configuration files list empty when used in Sync task

Hello,

As I’m not sure if it’s a bug or a configuration issue on my side, I would like to ask for help here before submitting a bug on GitHub.

I have the following project structure using Gradle wrapper and Gradle 4.7 (I have omitted Gradle wrapper files in the list below). I have also posted this code in a GitHub repository
.
build.gradle (empty file)

settings.gradle:

rootProject.name = 'dist-test'
include 'module-a'
include 'dist'

module-a/build.gradle:

apply plugin: "java"

configurations {
  moduleFiles
}

task generateModuleFiles << {
    def outputDir = file("${buildDir}/module-files")
    outputDir.mkdirs()

    def moduleFile1 = file("${outputDir}/module-a-file-1.txt")
    def moduleFile2 = file("${outputDir}/module-a-file-2.txt")

    moduleFile1.write("Module A file 1")
    moduleFile2.write("Module A file 2")

    artifacts {
        moduleFiles moduleFile1
        moduleFiles moduleFile2
    }
}

dist/build.gradle:

configurations {
  generatedFiles
}

dependencies {
    generatedFiles project(path: ':module-a', configuration: 'moduleFiles')
}

task buildDist(type: Sync, dependsOn: [':module-a:generateModuleFiles']) {
    doFirst {
        println "Dist generated files"
        println "${configurations.generatedFiles.files}"
    }

    destinationDir = file("${buildDir}/distribution")

    into('generatedFiles') {
        from configurations.generatedFiles
    }

    into('otherDir') {
        from "${rootDir}/build.gradle"
    }
}

The issue with my configuration is that when I run ./gradlew :dist:buildDist with the following block uncommented in dist/build.gradle:

    into('generatedFiles') {
        from configurations.generatedFiles
    }

then doFirst block prints an empty list of files available in generatedFiles configuration. On the other hand when I comment out into('generatedFiles') block then doFirst block correctly prints two files available in generatedFiles configuration (dist-test/module-a/build/module-files/module-a-file-1.txt and dist-test/module-a/build/module-files/module-a-file-2.txt)

Is it a bug or do I have an issue in my configuration?

Best regards,

Your configuration of the generateModuleFiles task is problematic. You don’t configure the artifacts until the task executes, but the definition of the artifacts should be what causes the task to run from the intra-project configuration dependencies.

Also, << is deprecated. You should use doFirst{} / doLast {} and only include the actual work in the task action. The artifacts should be configured at configuration time, and either use task outputs or declare builtBy to make sure the appropriate relationship is established.

Thank you @jjustinic. Based on your help I came up with the following solution:

In module-a/build.gradle:

task generateModuleFiles() {
    def outputDir = file("${buildDir}/module-files")
    def moduleFile1 = file("${outputDir}/module-a-file-1.txt")
    def moduleFile2 = file("${outputDir}/module-a-file-2.txt")

    outputs.file moduleFile1
    outputs.file moduleFile2

    doFirst {
        println("Generating module A files")
        outputDir.mkdirs()
        moduleFile1.write("Module A file 1")
        moduleFile2.write("Module A file 2")
    }
}

and use these output files directly in another module:

task buildDist(type: Sync) {
    doFirst {
        println "Dist generated files"
        println tasks.findByPath(':module-a:generateModuleFiles').outputs.getFiles().getFiles()
    }

    destinationDir = file("${buildDir}/distribution")

    into('generatedFiles') {
        from tasks.findByPath(':module-a:generateModuleFiles')
    }

    into('otherDir') {
        from "${rootDir}/build.gradle"
    }
}

While your generateModuleFiles task looks a lot better now, I would still recommend that you maintain the artifacts in the producing project, and consuming them the way that you were previously. While it may seem unnecessary, it is better practice to couple your dist project to the exposed artifacts, which are designed for this, rather than an implementation detail of the producing project. Directly using the task output from another project introduces unnecessary coupling.

Am I correct that when using artifacts I would have to use zip or jar files to package all the files generated by my task? Would it mean I would have to have two tasks in module-a, one that produces the files and another that zips them and publish as an artifact?

No, a File is sufficient. With what you have now in module-a/build.gradle, you could just add:

configurations {
    moduleFiles
}

// current code

artifacts {
    generateModuleFiles.outputs.files.each { outputFile ->
        moduleFiles file(outputFile), { builtBy generateModuleFiles }
    }
}
1 Like

It works great! Once again thank you for your help!