How do I get a task to execute of type Zip using 'dependsOn'? - gradle

I am trying to make my task zipGui execute on build target, but the only way I seem to be able to execute a task is if I remove the (type: Zip) from the task definition.
This snippet fails to execute the zipGui task at all:
task zipGui(type: Zip) {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
And this executes the zipGui but it doesn't know anything about how to zip files:
task zipGui {
doFirst {
println "==================== Zipping GUI components"
}
doLast {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
}
}
build.dependsOn zipGui
This is a stripped down fragment of the overall build.gradle.
How can I get zipGui to execute as a dependency of the build?
EDIT: here is more of the real build.gradle without me stripping things out to simplify the question:
task copyCert(type: Copy) {
from '../../../install'
into 'assets/certs/root'
include 'ca.pem'
doFirst {
println "==================== Copying root cert into assets"
}
}
task copyGui(dependsOn: copyCert, type: Copy) {
from '../../web/gui'
into 'assets/htdocs'
include '**/*.html'
include '**/*.css'
include '**/*.js'
include '**/*.wav'
include '**/*.tmpl'
include '**/*.png'
include '**/*.gif'
include '**/*.jpg'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
doFirst {
println "==================== Copying gui components into assets"
}
}
task zipGui(dependsOn: copyGui, type: Zip) {
FileTree zip = zipTree('assets/htdocs/gui.zip')
from 'assets/htdocs'
doFirst {
println "==================== Zipping GUI components"
}
}

Check details of zip task here: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html
task zipGui(type: Zip) {
archiveName = 'gui.zip'
from("$projectDir/../../../install") {
into 'assets/certs/root'
include 'ca.pem'
}
from("$projectDir/../../web/gui"){
into 'assets/htdocs'
exclude '**/*.DS_Store'
exclude '**/.gitignore'
exclude '**/.thumb'
exclude '**/build'
} // Just include or exclude is enough here as you indicated.
destinationDir(file("$buildDir/libs"))
}

Your task simply has nothing to do.
Gradle tasks are only executed, if they have something to do. If there is nothing to do (e.g. no files to zip), a task is skipped. There are multiple reasons for a task to have nothing to do. The main reason are up-to-date checks to prevent Gradle from doing the same thing what was done during the last invocation, at least as long as the task outputs are still available and the task inputs did not change.
However, in your specific case, the task has nothing to do, because at the time the task executes, no input files are specified at all. Files to include into the zip file can be added via from, which you do use. But, you use it inside a doLast closure, which is executed after the task actions (where the zipping takes place) were executed. Therefor, when running the zip process, the configuration did not take place.
You could use the regular configuration closure of the task or even the doFirst closure to configure your task:
task zipGui(type: Zip) {
from 'assets/htdocs'
}
// OR
task zipGui(type: Zip) {
doFirst {
from 'assets/htdocs'
}
}
Some more remarks:
The line FileTree zip = zipTree('assets/htdocs/gui.zip') does absolutely nothing, because the created file tree is not queried. You need to either access the files of the file tree manually or pass it to a task, e.g. your zip task: from zipTree('assets/htdocs/gui.zip')
In the second example, the task is executed because for normal tasks there are no automatic checks if there is any work to do, since a normal task can basically do everything the user makes it do.
I'm not quite sure, but I think the second example should throw an exception, because a normal task without a type does not provide a from method.
To get the status of each executed task and more insight generally, use the command line parameter --console=plain. The parameters -d and --stacktrace / --full-stacktrace may be used to get more information on errors.

Related

Error using dependsOn to call task from another gradle file

I am working with a build.gradle file that has multiple ways to specify executions for a task - setup. To call a task from another gradle file - runtests.gradle, I created a task - testTask and added task dependency using dependsOn, but this implementation does not seem to work and giving out an error like :
Could not find property 'testTask' on root project 'GradleFile
My build file looks like this :
build.gradle
task setup(dependsOn: testTask) <<
{
println "In main execution"
}
// new task
task testTask(type: GradleBuild) {
if (getEnvironmentVariable('RUN_TEST').equalsIgnoreCase("true")) {
buildFile = "../Behave/runtests.gradle"
tasks = ['mainTask']
}
else {
println "Exiting runTests Task"
}
}
setup.doFirst {
println "In first execution"
}
setup.doLast {
println "In last execution"
}
D:\>gradle -q GradleFile/build.gradle setup
I am not looking to make much changes to existing tasks, so is there any other workaround I should try?
I have been through many links but could not find anything that suits this scenario. Looking for suggestions please.
Gradle is sensitive to the ordering of tasks in the build script if a task instance is given in the dependsOn. The task setup depends on task (instance) testTask which, at the moment the build script is compiled, doesn't exist yet. The most common options to solve the issue are:
Define task setup below testTask:
task testTask(type: GradleBuild) {
}
task setup(dependsOn: testTask) {
}
Use a relative path to the task, i.e. the task's name, in the dependsOn
task setup(dependsOn: 'testTask') {
}
task testTask(type: GradleBuild) {
}
Please find more details in Javadoc of Task.

Copy task doesn't fail (or log) when source file is missing

My Copy task copies multiple sources:
For example:
task initConfig(type: Copy) {
from('src/main/config') {
include '**/*.properties'
include '**/*.xml'
filter(ReplaceTokens, tokens: [version: '2.3.1'])
}
from('src/main/config') {
exclude '**/*.properties', '**/*.xml'
}
from('src/main/languages') {
rename 'EN_US_(.*)', '$1'
}
into 'build/target/config'
}
When one of the sources doesn't exist, the task doesn't fail nor log an error.
Using "inputs.sourceFiles.empty" as a solution works just for a single source:
Fail Gradle Copy task if source directory not exist
Using assert to each source...is not an elegant solution since I have a lot of sources.
assert file("bla bla").exists()
How can I fail the task when one of the sources is missing?

gradle zip task only creates zip in configuration phase

When I try to create a zip file in the execution phase of a Zip typed gradle task no zip file is created.
If I perform the same in the configuration phase (leaving out the doLast statement), the zip file is created without problems.
The doLast block is called since the println statement is shown in the output logging.
The reason that we need to generate the zip in the execution phase is because the file which needs to be compressed is the result of the compile execution.
I have also tried to solve this with a jar task, but this gives me similar problems.
Here's the code:
task createClassPathJar(type: Zip) {
dependsOn("createManifest")
from("${projectRoot}") {
include "MANIFEST.MF"
}
archiveName = "dummy.jar"
doLast {
destinationDir(file("${projectRoot}"))
archiveName = "zipfile.jar"
println "executing phase createClassPathJar. archiveName: " + archiveName
}
}
Can someone help me here ? I'm using Gradle v6.4.1.
You cannot use doLast to configure the action of your task, because it will be executed after the particular action (in this case the zipping) has run. Either use a doFirst closure or setup your task configuration in a way that it does not depend on other configurations:
As an example, depending on how properly your task createManifest defines its output, you may use it directly to define the Zip task content using "from createManifest".
I guess your reason for using a doLast closure is the call to destinationDir that is based on a variable. Instead, you may just use a closure that evaluates the variable lazily:
task createManifest {
outputs.file('path/to/MANIFEST.MF')
}
task createClassPathJar(type: Zip) {
from createManifest
archiveName = 'zipfile.jar'
destinationDir = file({ "${projectRoot}" }) // or just file({ projectRoot })
}
Try with doFirst instead of doLast:
task createClassPathJar(type: Zip) {
dependsOn("createManifest")
from("${projectRoot}") {
include "MANIFEST.MF"
}
archiveName = "dummy.jar"
doFirst {
destinationDir(file("${projectRoot}"))
archiveName = "zipfile.jar"
println "executing phase createClassPathJar. archiveName: " + archiveName
}
}
I don't know about your case but I tried myself by changing the archive name to the current time in the doFirst block, and the archive name was effectively matching the time of execution and not of configuration.

Gradle: Not able to run zipTree in doFirst block

I am trying to explode some jar files in a doFirst block as follows -
task copyBinaries(type: Copy){
def Jar1 = ""
def Jar2 = ""
def Jar3 = ""
doFirst {
Jar1 = configurations.Lib1.singleFile
Jar2 = configurations.Lib2.singleFile
Jar3 = configurations.Lib3.singleFile
}
inputs.files configurations.Lib1
inputs.files configurations.Lib2
inputs.files configurations.Lib3
from(zipTree(file(Jar1))) {
into('jar_folder1')
}
from(zipTree(file(Jar2))) {
into('jar_folder2')
}
from(zipTree(file(Jar3))) {
into('jar_folder3')
}
into('build/libs/')
}
}
In order the avoid resolution of dependencies in the configuration phase, I am extracting the file names from the configurations in the doFirst block. The problem is, since the Copy task needs the source and destination of copy during configuration phase, gradle sees Jar1, Jar2, Jar3 as empty strings and throws and error.
I am looking for a way to let the Copy task receive arguments (source file) during execution phase (after doFirst block is executed).
How can I address this situation? Thanks.
I've had a similar situation where I had to copy from a path that was created as output of another one.
I solved this with just declaring the copy logic in my task's doLast block:
task copyStuff {
doLast {
copy {
from zipTree('path/to/archive')
into 'destination/dir'
}
}
}
You just have to make sure, that your task runs after the task that creates the needed outputs with using dependsOn for example.

Running task after only if another task is not up-to-date

I have a task that I want to run in gradle after the basic 'classes' task runs, but only if it was not UP-TO-DATE.
How can I model this in gradle?
The problem I'm trying to solve is that I have a wsgen task that I use to generate a wsdl file. The source that is used as a basis of the wsdl has changed, but the wsgen task still says it's up-to-date since the output file exists. I want to delete the wsdl file if the build/compile task ran and was not up-to-date.
Let's say I have my task as follows:
task deleteGeneratedWsdl() {
// delete file here.
}
// I Only want this to run if classes was not UP-TO-DATE
deleteGeneratedWsdl.dependsOn classes
Here is my wsgen task:
task wsgen() {
def inputDir = file('build/classes/main')
def dstDir = file('build/wsdl')
def outputWsdl = file('build/wsdl/MyWSDL.wsdl')
inputs.dir inputDir
outputs.file outputWsdl
doLast{
dstDir.mkdirs()
ant {
taskdef(name:'wsgen',
classname:'com.sun.tools.ws.ant.WsGen',
classpath:configurations.jaxws.asPath)
wsgen(keep:true,
destdir: dstDir,
genwsdl:'true',
classpath:'build/classes/main;../UTIL_PROJ/build/classes/main',
sei:'my.path.SourceFile')
}
}
}
wsgen.dependsOn classes
build.dependsOn wsgen

Resources