How to copy dependencies so Gradle can possibly report "UP TO DATE"?


(davidmichaelkarr) #1

I have a small multi-project build. It has three subprojects, two that create WARs, and one that builds a Docker image using the produced WARs. In order to obtain the WAR files for the image build, I have to use some slightly obscure code to obtain and copy the artifacts. Thanks to other people in the forum, I was able to get the build working to this point.

Now I’m realizing that my task for copying the dependencies never reports “UP TO DATE”, and as a result the more expensive task of building the image never reports the same.

I’d like to examine whether it’s possible to fix my somewhat complex copy task so that Gradle might possibly detect that there’s nothing to do.

My current task looks like this:
`task copyDependencies(dependsOn: [":ordersService:build", “:ordersGUI:build”]) << {
project.copy {
from "src/docker"
into "${buildDir}/context"
include "Dockerfile"
include ".properties"
include "
.txt"
include “.excerpt"
include "
.sh”
}

configurations.runtime.setTransitive(false).resolvedConfiguration.resolvedArtifacts
.each { artifact ->
project.copy {
from artifact.file
into "${buildDir}/context"
rename { “${artifact.name}.${artifact.extension}” }
}
}
}`

I would think I could clean this up slightly by moving the first “project.copy” call to a separate Copy task and depending on that task. I assume that it’s the imperative use of “project.copy” that effectively makes it impossible for Gradle to ever report “UP TO DATE”.

The second part would be more complicated to fix, but I have a feeling I know how this might be done. Is it possible that instead of the imperative project.copy call, this could instead generate dynamic Copy tasks (in the loop, of course), and then have it dynamically add those tasks to the dependency tree? Does this have a chance of resolving this? Is it worth the trouble? :slight_smile:


(Stefan Oehme) #2

This is the exact same question as I answered in this thread.


(davidmichaelkarr) #3

Uh, ok, well, thanks for pinging that you responded on that thread, and that helped for that particular question, but this really is a different question. However, I’ve also already figured out how to do what I inferred earlier, but I’ve also determined that the effort is somewhat moot, as the most expensive task of building the image that this all depends on is not able to ever report “UP TO DATE” (I’ve already verified this with the authors of that plugin).

The following is what I now have, which incorporates the separation of tasks, making none of them “imperative”, and also including your advice on the other thread:
`dependencies {
runtime project(path: “:ordersService”, configuration: “war”)
runtime project(path: “:ordersGUI”, configuration: “war”)
runtime “oracle:ojdbc6:11.2.0.3”
}

task buildImage(type: DockerBuildImage) {
inputDir = file("${buildDir}/context")
imageId = “ssorderprocessingdashboard”
}

task copyDockerfileStaticFiles (type: Copy) {
from "src/docker"
into "${buildDir}/context"
include "Dockerfile"
include ".properties"
include "
.txt"
include “.excerpt"
include "
.sh”
}

configurations.runtime.setTransitive(false).resolvedConfiguration.resolvedArtifacts
.each { artifact ->
task “copy${artifact.name}”(type: Copy) {
inputs.files configurations.runtime
from artifact.file
into "${buildDir}/context"
rename { “${artifact.name}.${artifact.extension}” }
}
buildImage.dependsOn “copy${artifact.name}”
}

build.dependsOn buildImage
buildImage.dependsOn copyDockerfileStaticFiles`

One other thing that could be a little cleaner is the repeated reference to the dynamic task name of “copy${artifact.name}”. Is there some way I can do that without repeating the name?


(Stefan Oehme) #4

It is not, it was the same problem as in the other thread: Not defining inputs and outputs, leading to never being up-to-date.

Would be interesting what kind of task can never be up-to-date. Or did they just not consider it important enough to implement proper up-to-date checking?

This resolves all the dependencies at configuration time, definitely not what you want. You’ll want to create a single task which iterates over the artifacts at execution time and calls project.copy for each. Remember to declare the configuration as an input.

Also, that setTransitive call in there will mutate the runtime configuration, so you will get no more transitive dependencies in it at all. I’m pretty sure this is also not what you intended.


(davidmichaelkarr) #5

(I can’t figure out how to properly quote pieces of previous responses.)

Concerning whether it’s the same question: whatever. It’s moot now.

The task is “DockerBuildImage”, from Ben Muschko’s Gradle Docker Plugin. When I asked in an issue about making it “up-to-date-able”, they said they found it almost impossible to do, which upon reflection, I agreed with (example: common to put “yum update” in Dockerfiles before installing packages).

Concerning the “setTransitive(false)” call, that is exactly what I intended. I only need or want the top-level specified dependencies, being the two WAR files.


(davidmichaelkarr) #6

I still want to see if I can clear up the redundant reference to my dynamic task name (last sentence in my last post), but I did want to talk about the tradeoffs of this particular point you mention. I guess I can see some of the advantages of what you suggest, but I’d like to go into this in detail.

First of all, with respect to “configuration time” vs. “execution time”, you say “definitely not what you want”. Can you explain why that is?

Also, you indicate that what I should be doing is calling “project.copy” (at execution time). Isn’t it the case that this will always do the copy, even if the file is already there? By generating the tasks at configuration time, it’s possible the copy will be “UP TO DATE”.