The answer to our problem is actually simpler to reason about: reverse the logic.
Instead of thinking "where should I put those things so that it’s picked up by jar", think "let’s tell the jar
task that it also needs to pick up my resources".
All in all, it’s about properly declaring your task inputs.
Instead of patching up the output of another task (seriously, forget about this!), every single task must be thought as a function which takes inputs and produces an output: it’s isolated.
So, what are the inputs of our docsFileJar
? The resources we want to package. What are its outputs? The jar
itself. There’s nothing about where we should put the jar, we let Gradle pick a reasonable place for us.
Then what are the inputs of the jar
task itself? Well, it’s regular inputs plus our jar. It’s easier to reason about, and as bonus, it’s even shorter to write!
So let’s rewrite the code above to:
task docFilesJar(type: Jar, description: 'Package up files used for generating documentation.') {
archiveVersion = null
archiveFileName = "grails-doc-files.jar"
from "src/main/template"
}
jar {
from docFilesJar
}
Can you spot the difference? We got rid of the copy
in the docFilesJar
task, we don’t want to do this. What we want, instead, is to say "when you build the jar, also pick this docsFileJar
. And that’s what we’re doing by telling from docsFileJar
. Gradle is smart enough to know that when it will need to execute the jar
task, first, it will need to build the docsFilesJar
.
There are several advantages to this:
-
the dependency becomes implicit: if we don’t want to include the jar anymore, we just have to remove it from the specification of the inputs.
-
it doesn’t pollute the outputs of other tasks
-
you can execute the docsFileJar
independently of jar
All in all, it’s about isolating things from each other and reducing the risks of breaking a build accidentally!