Gradle always rebuilds war


(Paweł Róg) #1

Hi,
I’m building projects with gradle. Each time I trigger gradle build all war tasks are rebuilt (no code or resources changes in meantime). This is a little annoying because even if nothing has changed all war files are rebuilt. Do I have to configure war plugin somehow to avoid rebuilding war files?
Gradle scan says that the task was not up-to-date because war file has been removed. The same happens for some of Jar files. This is of course less annoying because jar files are smaller so it doesn’t take so much time to rebuild them. Maybe I’m doing something wrong with the configuration and for some reason, Jar’s and war files are rebuilt.

Thank you all for any advices in an advance


(Stefan Wolf) #2

Hi @prog8,

it seems like some kind of timestamped version is part of the filename for your jar. This is why Gradle cannot re-use the old jar. Please remove the timestamp from the filename. Note that this does not mean that the version of your project would change.
Maybe something like

tasks.withType(War) { war ->
    war.version = "2017"
}

might help.

Cheers,
Stefan


(Paweł Róg) #3

Hi @Stefan_Wolf. Thank you for your suggestions :slight_smile:

The timestamped version actually is based on git commit date and I expect it to change on commit but the problem is probably somewhere else. I think I found the reason. I had a task defined in allproject section and I didn’t think that it is triggered automatically.

  task cleanArtifacts {
    fileTree("${buildDir}/explodedWebapp").files.each { file ->
      delete(file)
    }
    fileTree("${buildDir}/libs").files.each { file ->
      delete(file)
    }
  }

(Stefan Wolf) #4

That indeed seems to be the problem. Nonetheless, I suggest you remove the version number from the archive name - so Gradle can decide if the Jar/War is up-to-date and it doesn’t have to necessarily rebuild it on every commit.


(Paweł Róg) #5

Thank you.
Is it even possible to declare a task which is not started automatically (start only on demand when explicitly triggered as gradle task123)?

Indeed this makes sense to get rid of timestamp from the version. It will probably make it easier to decide what should be deployed (war/jar modification date). Does it make sense?


(Stefan Wolf) #6

What do you mean by “Is it even possible to declare a task which is not started automatically”? Any task you add in your build script would not be triggered by any other task, as long as you don’t add dependencies on it (via dependsOn). Tasks added by the java or war plugin are already wired up, so that lifecycle tasks like build depend on e.g. the jar and the war task.

It makes sense what you are saying. Since Gradle can infer the actual files to deploy from the War tasks it probably does not matter, but it makes it easier to reason about. You also would not have problems in deploying a war from the same project twice to the same container (for two different commits).


(Paweł Róg) #7

That’s how I thought it works … and I never triggered cleanArtifacts explicitly or through dependsOn and it was somehow triggered because it indeed removed war and jar atrifacts.


(Stefan Wolf) #8

The task cleanArtifacts is missing doLast, so the desired action (delete all artifacts) is executed during configuration time, for every build. The actual task has no action.
The task should be defined as

task cleanArtifacts {
    doLast {
        fileTree("${buildDir}/explodedWebapp").files.each { file ->
          delete(file)
        }
        fileTree("${buildDir}/libs").files.each { file ->
          delete(file)
        }
    }
}

(Paweł Róg) #9

Thanks @Stefan_Wolf for explaining this :+1:


(uklance) #10

This could be simplified as

task cleanArtifacts(type: Delete)
   delete "${buildDir}/explodedWebapp"
   delete "${buildDir}/libs"
}

Note that in this instance, Delete.delete(…) is taking precedence over Project.delete(…). Confusing, I know


(Paweł Róg) #11

Thanks @Lance. What if I have a task where I have to filter files by a criterion (e.g. regex)? Can I still use type: Delete or the previous solution will be better?


(uklance) #12

You could use Delete and FileTree.matching(…)

task cleanArtifacts(type: Delete) {
   delete fileTree("${buildDir}/explodedWebapp").matching {
      include '**/foo/**' 
      exclude '**/*.xml' 
   } 
}