Incremental compilation doesn't work for inputs of type java.util.regex.Pattern

(Dimitar Dimitrov) #1

I am writing a custom task, and I am trying to use the incremental annotations by denoting the inputs and outputs. Here is an example:

class Notifier extends DefaultTask {
        @Input Serializable matcher
        @InputFile File file
          @TaskAction void notifyIfMatching() {
            if (matcher.isCase(file.text)) {
                // send an email
      task notify1(type: Notifier) {
         file = file("foobar")
        matcher = ~/.*foo.*bar.*/
              task notify2(type: Notifier) {
         file = file("foobar")
        matcher = { it ==~ ~/.*foo.*bar.*/ }

When I run twice: gradle notify1 notify2

I can see that notify1 is marked as UP TO DATE, while notify2 is evaluated every time. If I run with -d, I cans ee this is because of the “matcher” is presumed different.

(Peter Niederwieser) #2

This is the expected behavior, as per the ‘equals’ contract of ‘groovy.lang.Closure’. To get the desired behavior, you’ll probably have to redesign the API (e.g. accept a ‘String’ or ‘Pattern’).

(Dimitar Dimitrov) #3

I am sorry, bad example - in practice I was using a Pattern literal, and I changed it to string later.

In the case of Pattern literal, Gradle thought that the value never matched the one from the build cache. What would be the way to debug this?

Unfortunately, I can not post the actual code.

(Peter Niederwieser) #4

Turns out that ‘Pattern’ doesn’t implement ‘equals’ either. Best use a String.

(Dimitar Dimitrov) #5

That’s what I am doing now, though in general I like being able to use GroovyObject.isCase() for specifying criteria.

May I suggest that Gradle adds special behavior for checking Input values in case of Pattern instances and instead of invoking the equals() on the instance itself, it compares against the return value of value.pattern() method?

This can be generalized to a registry of comparators per type, defaulting to equals() comparator.

(Peter Niederwieser) #6

It’s a possibility, though String seems to be a better fit here (build scripts shouldn’t have to be concerned with constructing ‘Pattern’ instances). Raised as GRADLE-2868.

(Dimitar Dimitrov) #7

Defining the matcher as object is fairly common pattern in Groovy, i.e. the GroovyObject.grep(), the implementation of case patterns, etc.

This allows to write less code in the task implementation and retain the flexibility to specify the matcher as string literal, regex, or closure.

I can understand that closures can not be reasonably compared, but having it working for strings and regexes covers a lot of use cases.