Task executes even if outputs.file hasn't changed

I’ve a task in a parent module that downloads the PMD ruleset before any PMD tasks are run. I thought that if I set the outputs.file to the downloaded file, the task would not download it multiple times. This, however, does not work the first time the task is ran; all the sub modules download the file even though after one has done so, the others shouldn’t have to. The later runs are as expected whereas the task is skipped as UP-TO-DATE. I’m working around it by using

onlyIf { !pmdRuleSetLocation.exists() }

.

task downloadPmdRuleSet {
    onlyIf { !pmdRuleSetLocation.exists() }
      doLast {
        def src = "${projectURI}/src/main/config/pmd/ruleset.xml"
          pmdRuleSetLocation.parentFile.mkdirs()
          println "Downloading ${src} to ${pmdRuleSetLocation.absolutePath}."
          ant.get(src: "${src}", dest: pmdRuleSetLocation.absolutePath)
    }
}
  tasks.withType(Pmd) {
    it.dependsOn downloadPmdRuleSet
      it.onlyIf { !project.hasProperty('skipCoverage') }
    reports {
        xml.enabled = false
        html.enabled = true
    }
}

Sounds like you declared one task for each subproject then. A single task can’t execute more than once per Gradle invocation.

The pmd task is in a subprojects block - it needs to run for every project. The download task that pmd depends on doesn’t need to, but it does. It shouldn’t because once the file is downloaded, the output should be up to date.

Each task decides on its own whether it’s up-to-date. Hence you’ll have to make sure to only declare a single download task, not one per subproject.

Will try that. What I don’t understand is that of the output file of 2 tasks are the same, how can one be not up to date after another has ran?

It’s not clear to me why the second task should be up-to-date. The tasks could have different behavior or different inputs. Also, the decision is made individually for each task. In general, no two tasks should declare the same outputs. Otherwise they can overwrite each other’s outputs, which can lead to troubles.

So up to date is not only a function of output file but also input? That is not different too in this case, as you can see above. It’s really the same task declared in a subprojects block.

It’s a function of inputs and outputs. “The same task declared in a subprojects block” is a completely unrelated task from Gradle’s perspective.

Now that I’m trying to declare the download task once, I see a problem. As I described above, the build file is common and is “applied” to other projects. Every task declared in the build file gets included for each submodule of the applying project. Is there a way to not do that? I guess I can apply to a parent module outside of subprojects but my understanding is, then I’ll only be able to execute these “applied” tasks from the parent and not from each submodule. If there’s a way to force a task to associate with the rootProject only, that’ll work too.

OK, this is what I got working. Thanks for your help.

if (!rootProject.tasks.findByName('downloadPmdRuleSet')) {
    rootProject.tasks.create('downloadPmdRuleSet', {
        outputs.file pmdRuleSetLocation
          doLast {
            def src = "${projectURI}/src/main/config/pmd/ruleset.xml"
              pmdRuleSetLocation.parentFile.mkdirs()
              println "Downloading ${src} to ${pmdRuleSetLocation.absolutePath}."
              ant.get(src: "${src}", dest: pmdRuleSetLocation.absolutePath)
        }
    })
}