Task executes even if outputs.file hasn't changed


(Abhijit Sarkar) #1

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
    }
}

(Peter Niederwieser) #2

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


(Abhijit Sarkar) #3

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.


(Peter Niederwieser) #4

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.


(Abhijit Sarkar) #5

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?


(Peter Niederwieser) #6

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.


(Abhijit Sarkar) #7

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.


(Peter Niederwieser) #8

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.


(Abhijit Sarkar) #9

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.


(Abhijit Sarkar) #10

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)
        }
    })
}