I just upgraded from Gradle 9.6.1 to 9.7.0 and got...
# kotlin-dsl
o
I just upgraded from Gradle 9.6.1 to 9.7.0 and got into a problem with the Copy action. The rename function has a mismatch with the changed declaration of
Transformer
interface... See this:
Copy code
import org.gradle.api.Transformer
import java.io.Serializable

tasks.register<Copy>("demo") {
    from(layout.projectDirectory)
    into(layout.buildDirectory.dir("out"))
    rename(MyTransformer())
}

private class MyTransformer : Transformer<String, String>, Serializable {
    override fun transform(name: String): String = name
}
This should compile. But it does not. In Gradle < 9.7.0, the
Transformer
interface was declared
<OUT: Any?, IN>
, now it's
<OUT: Any, IN>
....
v
Hm, that's strange. The JavaDoc of
rename
says you can return
null
to use the original name. The method according to JavaDoc is
Copy code
rename(Transformer<@Nullable String, String> renamer)
and
Transformer
according to JavaDoc is
Copy code
Transformer<OUT extends @Nullable Object, IN>
So it seems to me it should still be nullable in 9.7.0
Actually, for me your example also does not compile in 9.6.1. But in 9.6.1 I can change it to
MyTransformer : Transformer<String?, String>, Serializable
and then it works, which does not work in 9.7.0 either. 😵‍💫
The bundled Kotlin was updated from 2.3.21 to 2.4.0, maybe the problem is related to that
But anyway, as the same code works in 9.6.1 but no in 9.7.0 this is a breaking change, so most probably not intentional and you should report it to Gradle.
o
My code for 9.6.1 defined a custom Transformer class (so that config cache works). But that needed a change from
Transformer<String?, String>
to
Transformer<String, String>
- which is ok and understandable. But then,
rename
was not able to take that anymore due to its signature requiring a nullable
OUT
... I actually thought this Slack was for bug reporting to Gradle... 😄
v
But that needed a change from
Transformer<String?, String>
to
Transformer<String, String>
- which is ok and understandable
I'd say that is neither ok nor understandable. I'd say the bug is that you need to change from
String?
to
String
there. I don't understand why you should need to change it, can you explain to me why it is understandable for you? Also, as I said, if you do
Transformer<String, String>
in 9.6.1 it does not work either. And if something worked in 9.6.1 (the
String?
variant) it should not break before being deprecated in some Gradle 9.x and before a Gradle 10.x.
I actually thought this Slack was for bug reporting to Gradle... 😄
No, this is a community slack where mainly users like me help fellow users. For bug reporting and feature requests there is GitHub issues.
o
Well, they changed the signature of Transformer - so, that's understandable for me. If they deliberately wanted to change it. And my code can be fixed easily. But they forgot the rename method, I guess.
v
Well, they changed the signature of Transformer
No they didn't and they shouldn't without first deprecating and then in a major version breaking
Everything else would against their compatibility guarantees
o
Ok, why don't they test such things then...? All I did is go from 9.6.1 which worked fine to 9.7.0 and it did not work anymore.
Ok, why don't they test such things then...?
Probably because you cannot test each and every edge case? If you don't have a subclass but a lambda, it probably works without problem. And as I said, as the siganture did not change, I suspect it might be the update of the bundled Kotlin version and maybe a bug in that one.
Gradle does have extensive tests, but you never catch all potential bugs no matter how sophisitcated your test suite is.
o
Yes, of course - but a lambda cannot be serialized. Hence, to make config caching work, I needed a class.
v
That's why I said you should report it as breaking change and then they probably get out a hotfix version where this is fixed usually.
Yes, of course - but a lambda cannot be serialized
That's not necessarily true
o
The more I work with Gradle, the more I get frustrated from basic lack of good programming habits on their side... 😉
v
That was true long ago for Java lambdas and Kotlin lambdas compiled to Java lambda bytecode
But that is not true anymore since quite some versions
o
Ok, I had a normal lambda there and when enabling config cache I get errors that it cannot be serialized. Hence, the class.
v
The more I work with Gradle, the more I get frustrated from basic lack of good programming habits on their side.
I really don't know what you mean, can you elaborate? I have the quite opposite experience
Ok, I had a normal lambda there and when enabling config cache I get errors that it cannot be serialized. Hence, the class.
What was the exact error?
Can you show an MCVE?
o
Copy code
1 problem was found storing the configuration cache.
- Task `:copySapDependencies` of type `org.gradle.api.tasks.Copy`: cannot serialize Gradle script object references as these are not supported with the configuration cache.
  See <https://docs.gradle.org/9.7.0/userguide/configuration_cache_requirements.html#config_cache:requirements:disallowed_types>
v
Yeah, that's not a problem of the lambda
Transforming your example to lambda, that is
Copy code
tasks.register<Copy>("demo") {
    from(layout.projectDirectory.dir("foo"))
    into(layout.buildDirectory.dir("out"))
    rename { it }
}
works perfectly fine
The error says that you captured a reference to the script object in your lambda and that is not supported
o
Of course. My rename function is a little bit more complicated.
v
Like for example
Copy code
val foo = ""
tasks.register<Copy>("demo") {
    from(layout.projectDirectory.dir("foo"))
    into(layout.buildDirectory.dir("out"))
    rename { foo }
}
o
Hence, the solution was a class with proper inputs/outputs that can be serialized.
v
That's overkill, just make sure you don't have script object references
The example I just have shown for example could be fixed using
Copy code
val foo = ""
tasks.register<Copy>("demo") {
    from(layout.projectDirectory.dir("foo"))
    into(layout.buildDirectory.dir("out"))
    val foo = foo
    rename { foo }
}
o
Ok, it has references to top level boolean values...
v
As you store the script object field
foo
into a local variable
foo
and use that in the lambda
o
ok, I try that!
v
Ok, it has references to top level boolean values...
Exactly, and that is the problem
This would also work:
Copy code
class Constants {
    companion object {
        val foo = ""
    }
}
tasks.register<Copy>("demo") {
    from(layout.projectDirectory.dir("foo"))
    into(layout.buildDirectory.dir("out"))
    rename { Constants.foo }
}
But nevertheless, please report the breaking change as bug
o
Cool, thanks. It's a problem of the lambda WITH captured values, not of the lambda itself. Got it.
v
Think of the Kotlin Script content as the body of a class, with all "top level" functions and variables being fields and methods of the class and the code being the body of a function. That actually is roughly what it ends up indeed. So if you access one of those "top-level" functions or variables, you access them on the instance of that script class. And that is, what is not allowed as the script instance cannot go into CC.
o
The really strange thing is this. This is my original code that works with 9.6.1. You see it complains about the String?. Looking at the declaration of Transformer though... does NOT show an upper bound to Any....
v
You are not looking at the correct source
The last time it had that signature was in Gradle 7
Since 8.0 it is
OUT extends @Nullable Object
Since 8.0.0-M6 to be precise
But anyway the behavior broke from 9.6.1 to 9.7.0 which is why I asked you to report it 🙂
Please also post the link here for reference once you created the issue 🙂
o
The source is the one that IntelliJ links to. Why should that be wrong?
It's not really source. It's byte code. And it just shows it's Any?
No restriction as it's Java.
v
I don't know why yours shows the wrong source, my crystal ball is in the repair shop. Mine shows the correct sources:
No restriction as it's Java.
The restriction comes from the nullable annotation that Kotlin is taking into consideration
If sources are not linked for whatever reason and it just shows the decompiled class as it seems for you as JavaDoc is also missing, then IntelliJ is just showing the pure code and misses the annotation it seems. That's maybe something to complain to JetBrains about