Hi All,
I’m trying to write a base plugin for our Gradle based projects. Idea is to reduce boilerplate build code.
Currently I’m fighting an “always up-to-date” problem with one of my provided base tasks.
Given
class BasePlugin implements Plugin<Project> {
void apply(Project project) {
...
project.task('unpackBuildTools', type: Copy) {
group 'Build'
description 'Unpacks resolved build related tools into the ${project.buildDir} directory.'
doLast {
project.configurations.tools.resolvedConfiguration.resolvedArtifacts.each { toolDependency ->
copy {
from zipTree(toolDependency.file)
into "${project.buildDir}/${toolDependency.name}"
}
}
}
}
...
When I run a build that uses this plugin the unpackBuildTools
task is always UP-TO-DATE
and I’m running out of ideas…
Any insights would be highly appreciated.
You’re not actually using the copy task, you’re explicitly calling project.copy
in a doLast
. Net effect is that there are no task inputs and hence no up-to-date checking.
Try this:
project.task('unpackBuildTools', type: Copy) {
group 'Build'
description 'Unpacks resolved build related tools into the ${project.buildDir} directory.'
project.configurations.tools.resolvedConfiguration.resolvedArtifacts.each { toolDependency ->
with project.copySpec({
from zipTree(toolDependency.file)
into "${project.buildDir}/${toolDependency.name}"
})
}
}
Hi and sorry for my delay.
I didn’t realize that I was actually doing a project.copy
, thanks for pointing that out…
I’m having trouble running your suggestion due to;
Cannot change dependencies of configuration ':tools' after it has been resolved.
I’ve tried several different ideas but seem to come back to constructs like my original one. Partly because I do want the lazy eval of the configuration and I have no hard requirement on task type.
Since normal tasks are run every time if they do not specify intputs
/outputs
I have the following running now;
project.task('unpackBuildTools') {
group 'Build'
description 'Unpacks resolved build related tools into the ${project.buildDir} directory.'
doLast {
project.configurations.tools.resolvedConfiguration.resolvedArtifacts.each { toolDependency ->
project.copy {
from project.zipTree(toolDependency.file)
into "${project.buildDir}/${toolDependency.name}"
}
}
}
}
If you put it all under a single root directory you could use the configuration as an input and the root dir as an output
project.task('unpackBuildTools') {
inputs.files project.configurations.tools
outputs.dir "${project.buildDir}/unpack"
...
doLast {
...
copy {
...
into "${project.buildDir}/unpack/${toolDependency.name}"
}
}
}
@see TaskInputs.files(…)
@see TaskOutputs.dir(…)
Another option is to delay the creation of the task so that the configuration is resolved later
project.afterEvaluate {
project.task('unpackBuildTools', type: Copy) {
...
}
}
Works like a charm.
Creating the task in afterEvaluate
…why didn’t I think of that!? Maybe too obvious…
Thank you very much!
Patrik