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!