UPTODATE feature for custom jar file - how to configure it?

I want to create a jar file with the UPTODATE feature.

Here is the snippet of my gradle file

task jarServer(type: Jar, dependsOn: classes) {
    inputs.files classes.outputs.files
    inputs.files configurations.internal
    outputs.files file("build/libs/betsy-vms.jar")
      from files(sourceSets.main.output.classesDir)
    from files(sourceSets.main.resources)
    from { configurations.internal.collect { it.isDirectory() ? it : zipTree(it) } }
      dependsOn configurations.internal
      // results in build/libs/betsy-vms.jar
    classifier = 'vms'
    baseName = 'betsy'
      manifest {
        attributes 'Main-Class': 'betsy.virtual.server.BetsyVirtualMachineServer', 'Implementation-Title': 'Betsy-VirtualMachineServer', 'Built-Date': new Date()
    }
}

In my case, the jar is recreated for each build even if nothing has changed - I would like gradle to behave that the jar is not rebuild if nothing has changed. Do you have any leads how to get gradle to skip rebuilding if nothing changed? I have tried to do this myself using inputs and outputs, but this did not work as expected.

You shouldn’t have to declare these inputs and outputs yourself, as ‘from’ and ‘into’ will do that implicitly. Run with ‘–info’ to find out why the task isn’t considered up-to-date. I’m not sure if manifest attributes are currently taken into account. If so, ‘new Date()’ will change the inputs every time.

Thank you for your answer. When I modify the build script according to your suggestions, I get the following:

task jarServer(type: Jar, dependsOn: classes) {
    from files(sourceSets.main.output.classesDir)
    from files(sourceSets.main.resources)
    from { configurations.internal.collect { it.isDirectory() ? it : zipTree(it) } }
      dependsOn configurations.internal
      // results in build/libs/betsy-vms.jar
    classifier = 'vms'
    baseName = 'betsy'
      manifest {
        attributes 'Main-Class': 'betsy.virtual.server.BetsyVirtualMachineServer', 'Implementation-Title': 'Betsy-VirtualMachineServer'
    }
}

This will use the UPTODATE feature. However, it takes the same amount of time for the last step, as all zip files are extracted and each file is added to the UPTODATE check. Do you have any leads on how to set the configurations jar files as an input and do the extraction after a UPTODATE check?

Not sure what you mean by “the last step”, but I don’t think it’s currently possible to do the up-to-date check on the unextracted zip files (only).

Sorry, the last step for me is the execution of the jarServer task. To illustrate my problem, I have copied the snippet of the --info output

:jarServer (Thread[main,5,main]) started.
:jarServer
Skipping task ':jarServer' as it is up-to-date (took 56.406 secs).
:jarServer UP-TO-DATE
:jarServer (Thread[main,5,main]) completed. Took 56.463 secs.

Thus, the time intensive part is the unzipping of the configurations.internal jar files. Is it possible to state that I want the configurations.internal files in the jar as well and then let some groovy code be executed right before the creation of the jar file?

Not sure I understand the question. You can execute additional code using ‘jarServer.doFirst { … }’. The fact that the up-to-date check may not be a time-saver for ‘Copy’ tasks is a known limitation. Perhaps you could declare an adhoc task (i.e. one without a type), declare its inputs/outputs manually, and then use ‘doLast { project.copy … }’ to get the copying done. This will give you more control over the input/output checks.

I now solved it by extracing the time intensive part into a separate task with specific inputs and outputs. Thank you for your guidance!

task internalClasses(dependsOn: classes) {
    inputs.files configurations.internal
    outputs.dir "build/internalClasses"
      doLast {
        copy {
            from { configurations.internal.collect { it.isDirectory() ? it : zipTree(it) } }
            into "build/internalClasses"
        }
    }
}
  /**
 * Creates jar file for the virtual machines.
 */
task jarServer(type: Jar, dependsOn: [classes, internalClasses]) {
    from files(sourceSets.main.output.classesDir)
    from files(sourceSets.main.resources)
    from files("build/internalClasses")
      // results in build/libs/betsy-vms.jar
    classifier = 'vms'
    baseName = 'betsy'
      manifest {
        attributes 'Main-Class': 'betsy.virtual.server.BetsyVirtualMachineServer', 'Implementation-Title': 'Betsy-VirtualMachineServer'
    }
}