Simple zip creation with dependencies

I would like to define a task (we’ll call “zip”) that creates a zip file containing my assembled jar file, some config files, and some doc files. Further, I want to gather these files into the zip and have the zip have a single folder path to these files.

Here is what my tasks look like:

// Gather the dist files to a single folder
task copyDistFiles(type: Copy, dependsOn: assemble) {
    from "${libsDir}"    // jar file(s)
    from 'doc'           // doc file(s)
    from 'config'        // config file(s)
    into "${buildDir}/dist_files"
}

// zip (into my_files.zip) the dist files and include the first level folder name
task zip(type: Zip, dependsOn: copyDistFiles) {
    baseName = 'my_files'
    from buildDir
    include 'dist_files/*'
}

build.dependsOn zip

This basically works for me. When I do a gradle -q build, the build dependency results in the files being copied and the zip being created as I want it. The zip file is located in build/distributions/my_files.zip and it contains dist_files/* where * are the files I want.

I have now two questions:

  1. What I really want is for the dist_files folder in my build directory to be temporary. After the zip file is created in build/distributions I don’t need that folder around. I can add some code to delete the folder, but is there a more canonical way to do this?

  2. Originally, I had the above tasks defined with << { rather than just {. I know that << { is shorthand for doLast and occurs at execution time, whereas, as shown above, the task contents run at configuration time. What is confusing to me is that when I use << {, the dependency doesn’t seem to work. If I do gradle -q build, gradle will say that the zip file is up to date even if it hasn’t been created yet. Why is that so?

Zip has a similar API to Copy, so you can skip copying to an intermediate directory first by configuring the zip task the same way as you would the copy task. You don’t need to specify a destination (an ‘into’) because it defaults to the root of the archive.

If your jar is coming from the jar task, you can just say:

task zip(type: Zip) {
   from jar
   from 'doc'
   from 'config'
}

zip will automatically depend on jar

Your task is considered up-to-date when you use doLast because the task has an empty list of inputs when we’re deciding to execute the task. And for this particular type of task, the inputs are marked as SkipWhenEmpty.

Thanks for the prompt reply.
That worked great, and makes perfect sense.

What determines the “list of inputs” for these particular tasks? I thought that since what they depend upon had changed, the doLast block would execute.

The list of inputs are the locations that are specified with each from, but the problem was timing.

The doLast {...} block adds a TaskAction which runs in the execution phase. Additionally, the Copy and Zip tasks already implement their own TaskAction which does the actual Copy / Zip work in the execution phase.

You have a chicken and egg problem if you configure the things that determine if the task should run in the phase that actually executes the actions. Your TaskAction won’t run to configure the task inputs unless the inputs have changed, but you have no inputs unless the TaskAction runs.

Thank you. That makes sense. I’ve been reading the Gradle documentation online, particularly the discussion on the build lifecycle, but how the pre-defined tasks work has still been a little mysterious to me. This helped a lot.