Dynamic MANIFEST.MF attribute (new Date()) causes Gradle to build the jar every time, even though nothing else changed

Hi. I have a jar task, where I add some MANIFEST attributes:

jar {

manifest {

attributes(

‘Implementation-Title’: project.name,

‘Implementation-Version’: project.version,

‘Build-Time-ISO-8601’: new Date().format(“yyyy-MM-dd’T’HH:mm:ssZ”)

)

}

}

The problem is with the timestamp - it causes the jar task to be executed every time. When I comment this attribute out, the jar task can be skipped. That’s a problem as this is an api module, and all others depend on it, and as the jar changes, all other modules are recompiled even though they don’t really need to. What can I do to also have the jar task skipped? I imagine gradle stores a snapshot of the attributes, and compares them with the new ones, and sees they are different (new Date()), and hence re-jars.

wujek

Interesting question. Removing the date from the inputs of the Jar task would solve the problem, but I’m not sure if this is currently possible. A pragmatic solution would be to generate the date entry only for, say, the release build, where it may not matter that everything is rebuilt.

Our longer-term goal is to allow you to query which exact inputs have changed. You’ll then be able to use this information to decide if a task is up-to-date. There is already ‘tasks.outputs.upToDateWhen {}’, but you won’t currently be able to tell if only the date changed.

True, that is better than what I did now - disable it altogether ;d

Is there a better solution available for this by now?

Nothing about the Jar task has added, but we’ve delivered the first version of the functionality that will empower this kind of fine grained incremental behaviour.

http://www.gradle.org/docs/1.6-rc-1/release-notes#incremental-tasks

Would there be a way to check which inputs have changed or with other words to find out if the MANIFEST.MF file is the only changed file? If this was possible, I should be able to use a StopExecutionException to bail leaving the existing jar unchanged?

It’s not possible to do what you need right now. I was pointing out that we are working towards supporting this kind of thing.

Thank you Luke - I’ll work around it.

I have done something similar to this.

I have the compileJava task setting a build data and then only running the jar task if build date is set. Might not be the best solution, but it gets around building new jar files due to an updated timestamp.

ext.buildDate = null
  compileJava.doLast {
                buildDate = new Date()
                jar.manifest {
                    attributes(
                            'Built-Date': project.buildDate
                            )
                }
            }
tasks.jar.onlyIf { return project.buildDate != null}
  jar.manifest {
            attributes('Implementation-Title': project.name,
            'Implementation-Version': project.version,
            'Built-By': System.getProperty('user.name'),
            'Built-JDK': System.getProperty('java.version'),
            'Built-Host': getHostname(),
            'Source-Compatibility': project.sourceCompatibility,
            'Target-Compatibility': project.targetCompatibility
            )
        }

I am using Gradle 1.5 and I don’t have this problem when using doFirst.

My task svnSync tries and connects to the SVN repository of my project, prints a warning if the project is not sync, gets the revision number and add it to the project.ext.fullVersion property.

If I don’t use doFirst, the manifest attributes are set at the initialization, before the svnSync task can set the project.ext.fullVersion property.

Is it a valid solution to your issue, or am I doing something wrong?

jar {
 it.dependsOn ":svnSync"
}
jar.doFirst {
 manifest {
  attributes \
   'Implementation-Title': project.description, \
   'Implementation-Version': project.ext.fullVersion, \
   'Built-By': System.getProperty('user.name'), \
   'Built-JDK': System.getProperty('java.version'), \
   'Build-Time': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
 }
}

I am using Gradle 1.5 and I don’t have this problem when using doFirst.

My task svnSync tries and connects to the SVN repository of my project, prints a warning if the project is not sync, gets the revision number and add it to the project.ext.fullVersion property.

If I don’t use doFirst, the manifest attributes are set at the initialization, before the svnSync task can set the project.ext.fullVersion property.

Is it a valid solution to your issue, or am I doing something wrong?

jar {
 it.dependsOn ":svnSync"
}
jar.doFirst {
 manifest {
  attributes \
   'Implementation-Title': project.description, \
   'Implementation-Version': project.ext.fullVersion, \
   'Built-By': System.getProperty('user.name'), \
   'Built-JDK': System.getProperty('java.version'), \
   'Build-Time': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
 }
}

I would not expect this to work correctly. I’d think that in the configuration phase the manifest would be different from the one last used and that should trigger a rebuild. I haven’t tried this yet though.

My project is very small, so I’m not especially concerned by this problem. I just noted that: I run ‘gradle clean jar’, it takes 20s, I wait some time then I run ‘gradle jar’, it takes 12s and ‘:jar UP-TO-DATE’ is displayed. Maybe I’m not using the same version of gradle or groovy, maybe my ‘build.groovy’ is so twisted that the output is baffling, maybe I’m too novice to understand it all ^^ .

The problem seems to be that the jar task will produce a manifest based on what it knows before executing the task. If you place manifest into jar.doFirst the empty manifest that is derived before execution is different from the one that was produced the previous time and results in the production of a new empty manifest. I would suggest a change to jar/ear/war tasks that prodive an option called delayManifest which will only attempt to produce the manifest when the jar needs to be built because of other changes excluding the manifest. The only time the manifest should be an input dependency for the jar task is when manifest from is used.