I’m curious about what is the more “gradle” way to implement a complex task, as multiple small tasks that depend on each other, or as a single task doing a lot of work.
This stems primarily from the fact that it’s often easier to configure individual tasks, but it’s harder to get an overview, and in my particular case, the tasks aren’t that meaningful on their own.
Example pseudo code:
task modifyZip << {
exec { delete old files }
ant.unzip { the files }
exec { operate on extracted files }
exec { more operations on extracted files }
ant zip { new archive }
}
versus:
task prepare { delete old files }
task unzipStuff(dependsOn:[prepare]) { unzip }
task operate(dependsOn:[unzipStuff]) { operations on extracted files }
task packItUp(dependsOn:[operate]) { zip }
task modifyZip(dependsOn:[packItUp]) // It would be weird to name ‘packItUp’ ‘modifyZip’, IMO.
The former makes it clear that this is a single operation, where the inputs and outputs aren’t terribly useful unless the whole thing completes, but I have to use lower-level constructs, and the log contains less details.
The latter makes the individual actions stand out, and I can run partial operations, and I can utilize pre-packed tasks such as Copy/Zip/Exec, etc. On the other hand, the dependency chain can become long, and there’s no meaningful re-use of each task outside the chain.
Or perhaps there’s a completely different way to go about this? (Custom task class?)
In almost all cases, the former is preferred. A task should do one thing. There are several reasons for this:
Creating “composite” tasks usually means depending on things like Project.copy(), Ant tasks, or other methods to compose your task. This means things like incremental build support are lost. In general, using the appropriate task type Copy, Zip, Exec, is preferred.
By doing the work in steps we can leverage incremental build support better in that we can skip work at a much more granular level.
It’s generally more clear what our build is actually doing as each task has a discrete set of inputs/outputs and single function.
I don’t really see this as being a problem. Build users don’t really need to know the dependency chain, they only need know what they want the build to produce, and which task is responsible for that. Best way to do that is to document your tasks by giving them a good description and assigning them to an appropriate group. By doing so, other “undocumented” tasks are automatically hidden from the task report when running gradle tasks.
Ah, the “undocumented tasks are hidden” feature would help here, that is true. I was concerned with pollution of the task space, as I tend to document all tasks for my own benefit. Comments would of course work just as well for that purpose.
If the chain of tasks happens to be a bunch of custom task classes in buildSrc. Is it possible to make one depend on the other? Or should we have a task of each type and link them in the script?
We wanted to expose in the script only the last one in the chain so that the script was more concise and only if you need to know the steps in detail you go to the definition of the actual tasks in buildSrc.
Yes. But this should probably be done in a plugin, which then gets applied to your build script. So the plugin would create the custom tasks, and add the dependencies.