What's the minimum supported JVM version? I'm get...
# testballoon
c
What's the minimum supported JVM version? I'm getting
Copy code
Caused by: java.lang.UnsupportedClassVersionError: opensavvy/prepared/runner/testballoon/SuiteKt has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1022)
at runtime, but no problems at compile time, which confuses me.
👀 1
m
There is no
org.gradle.jvm.version
in the .module file which explains the absence of compile time error
Looking at the Ktor .module file, it’s similar, my guess is that the KMP plugin does not set it
Interestingly though, the Java version is supposed to be 11
Might be a bug in compat-patrouille 🤔 , looking into it
c
Nevermind I think it's on my side, I have an intermediate module that forgets to set the JDK toolchain 😕
I'm surprised this doesn't lead to a compilation error
m
Nooooooooo, do not set the toolchain 😱
c
I know… maybe today is the day I switch
🙌 1
I have avoided -release until now because of JDKs pre-9, but now that Gradle doesn't support pre-9 JDKs, it may be time to finally switch
m
It’s time to say goodby to Java 8 🫡
c
finally
m
For build tools, there is little reason not to use Java 24
(soon Java 25 🤞 )
c
…you tell me, all my devices are on 25 and nothing works
m
I’ve been using Java 25 with some success
Dokka fails, I think that’s the main one
Gradle 9 complains but it’s still working 😄
Back to the original issue, this explains the absence of compile time error
c
but then again, there are a few funny things
Copy code
inline fun <reified T> foo() {
    println("Foo: ${T::class}")
}

foo<Any?>()
JDK 11:
Foo java.lang.Object
JDK 17:
Foo kotlin.Any?
👀 1
m
Interesting!
Same Kotlin compiler version?
c
yep, just setting the JVM toolchain version
not even in that module, too
m
Is that string put in the bytecode as a compile-time constant?
That’s a good puzzler
c
oh god that's horrifying
so what's happening is that before I set the toolchain, the tests were not running at all
😱 1
I thought they were successful, but no
m
(using a toolchain for running tests can be usefull actually so if this is where you’re setting the toolchain, I retract my earlier comment)
c
No, I have a global toolchain with
Copy code
kotlin {
    jvmToolchain(11)
}
m
Yea, that one is no good
c
I mean, could be worse.
So, does
-release
get added to the .module?
m
Not when using the KMP Kotlin Gradle Plugin
Only when using
org.jetbrains.kotlin.jvm
c
that's… weird, aren't they the same JAR?
m
No idea how KMP configures the Java compiler under the hood for JVM vs KMP. I’m assuming they are very similar indeed
But it’s not the only discrepency in KGP. KMP-KGP creates publications, JMP-JVM doesn’t for an example
c
With -release, how do you ensure Gradle downloads a JDK if the user doesn't have one new enough?
m
I’m not even talking about KMP-ANDROID 😱
With -release, how do you ensure Gradle downloads a JDK if the user doesn’t have one new enough?
Wait, I’m not sure what you mean
c
As in, if I want to produce a module with
-release 17
, but the user is running Gradle on JDK 11, what happens?
m
By default
compileJava
should fail
kotlinc
doesn’t use
javac
and so my guess is that it can probably produce 17 bytecode if you pass
-Xjdk-release=17
c
That's a pain though, then users have to figure out how to install a new JDK and to configure Gradle to use it. With toolchains, Gradle scans the installed JDKs for one compatible, and installs a new one if necessary.
m
kotlinc
!=
javac
IIRC, Kotlin doesn’t use the JDK to produce the bytecode
c
>
kotlinc
doesn’t use
javac
and so my guess is that it can probably produce 17 bytecode if you pass
-Xjdk-release=17
Sure, but a JRE 17+ is required to run the tests.
m
Yes
I would expect the tests to fail with your initial error then
If they are ignored, this is weird
c
I wish I could specify a toolchain as "11 or whatever the user has installed that is more recent but then add -release 11"
m
11 or whatever the user has installed that is more recent
I’d love that too, I might have made a FR somewhere, let me check
Mmm no, can’t find it. If you open an issue, please link it
c
I wonder if it's possible to make that into a Gradle plugin. Do we have programmatic access to the toolchains?
m
No idea
c
At least it should be fairly simple to do: • If Gradle is running on a JDK 11+, declare -release • If Gradle is running on a JDK 11-, declare a toolchain
I think I'll add that to my conventions
m
Just realized that
opensavvy/prepared/runner/testballoon/SuiteKt
is not a Test Balloon class so test balloon is fine (targets Java 11, as expected 😌 )
testballoon 1
c
yep, sorry
m
No pb!
> • If Gradle is running on a JDK 11+, declare -release Your
-release
is only for your tests classes, right? Whatever you publish on maven central, you should make sure to enforce a consistent JVM target, independent of the Gradle JDK (or else it’s easy to produce inconsistent binaries dependending the host that publishes)
If you’re doing Kotlin you might not even have to configure
-release
, it should be -Xjdk-release everywhere
Only reason I can think of to set javac
-release
is to shut off some KGP warning/errors
c
Sure, I mean setting both -release and -Xjdk-release
m
I would set
-release and -Xjdk-release
always and enforce a minimum version for the Gradle JDK
Actually, I would use https://github.com/GradleUp/compat-patrouille, that takes care of all of that 🐾
c
But then if you have a contributor that is on JDK 8 for some legacy projects, they can't compile the project without messing with their system
(I have actually lost possible contributors due to that multiple times, before I was using toolchains)
m
Really?
I’m ok to lose people that don’t want to install Java 11 on their machines I think 😅
If you care a lot about this, you can use Daemon JVM auto-provisioning instead of toolchains, that’s already better
For users, it will be the same result really except that Java 11 will be install in a different folder than if they do
brew install zulu-jdk11
c
I guess so. But then I'm setting it to JDK 24
🚀 1
m
Yes!!
In my projects, I just use a simple check to force a minimum version of the Gradle JDK: https://github.com/apollographql/apollo-kotlin/blob/main/settings.gradle.kts#L9
c
Actually that may solve my current JDK 25 problems as well, so that may be convenient even when I have more modern stuff.
💯 1
m
Yea, auto-provisioning is good, maybe I should lswitch to that as well, I have just been too lazy 😄
c
Sure, but the average user doesn't know enough about Gradle to know how to install a new JDK, make Gradle find it, run everything. Nor do they care when they're looking at some open source project.
m
Fair
Oh, one reason why I haven’t switched all my projects to the auto-provisioning daemon JVM is that I want to be able to specify a
min
version, not
exact
version.
I don’t want to download gazillions of JDKs
Just use 25 everywhere
Maybe I should file that issue
c
Yeah
All my projects depend on the same Git repo, so I can update all of them in a single commit
m
c
So:
Copy code
kotlin {
	compilerOptions.freeCompilerArgs.add("-Xjdk-release=${Versions.JAVA_COMPAT}")
}

tasks.withType(JavaCompile::class) {
	options.release = Versions.JAVA_COMPAT
}
? Or is there something else I need?
m
I literally wrote compat-patrouille so that I don't have to remember this
You probably want to set
jvmTarget
for kotlinc too, I'm not sure if
-Xjdk-release
is setting it
And if you have Android targets, you must not set
-Xjdk-release
(because there is no JDK on Android, just android.jar)
c
😕
it's such a Gradle thing that there isn't a simple config option for this
m
c
I mean, it's not a Gradle option
it's yet another basic feature built-in by a third-party
m
Yea, it can't be first party because it's split between Gradle/JetBrain/Google
It's-a-spaghetti
Re-reading that README, you'll also have to set
coreLibrariesVersion
(mmm no wait, I take that back that is for Kotlin, not JVM things)
c
also, I don't care about the Kotlin version, just the JVM version. I always use the Kotlin version from KGP.
👍 1
I'm trying to avoid depending on yet another plugin in my conventions because I currently spend more time maintaining my conventions than I spend maintaining my actual libs. Until recently, if I wanted to upgrade Kotlin, the process was: • Update opensavvy-gradle-conventions • Update opensavvy-playground • Update each project For most of everything I can now skip updating the conventions, but that's where the toolchains are
Between TestBalloon (to be fair, it's explicitly not stable yet) and Kotest, it's become a major pain to upgrade anything since K2
kodee sad 1
m
Fair enough
c
I will steal what I need from compat-patrouille though, hopefully that never needs to change 🙏
🤞 1
👍 1
I hope one day we can have a build tool that does that by default though
Maybe I'll do that when I'm done with MongoDB lol
😄 1
m
Maybe Amper has a better DSL there
c
as long as it doesn't have plugins, it's a no-go for any project I work on
same 1
…how wow this is so much worse than I thought
I'm reading the compat-patrouille code
So, if you have an Android target, you have to remove -Xjdk-release even for the other platforms?
m
Only for Android
Does it removes on the other platforms?
c
but I guess that makes sense, you can't apply the Java plugin in an Android library?
m
Yes, that part is expected
You can have java + Android
At least until AGP9 😬
You just can't set
-release
on Android because there is no
-release
Android is using
android.jar
, not your usual JRE
c
yeah
m
(This is where the compileSdk 35
removeFirst()
madness is coming from)
c
yep yep
So, if I have a Multiplatform library with just a JVM target, I can use -Xjdk-release, right, even if it's later consumed by Android?
But if I have a Multiplatform library that has both the JVM and Android targets, then -Xjdk-release should only be set on the KotlinCompile task? What does Android use to compile Kotlin code? Is it another task?
m
I can use -Xjdk-release, right, even if it's later consumed by Android?
Yes but your consumers may have issues if they don't desugar or if desugaring doesn't support the api (like
removeFirst()
)
if I have a Multiplatform library that has both the JVM and Android targets, then -Xjdk-release should only be set on the KotlinCompile task?
correct, only for the JVM task
c
Yeah, Prepared is stuck on JDK 11 because of this
m
What does Android use to compile Kotlin code? Is it another task?
No idea TBH
c
what a mess
nod 1
m
Probably Android uses a custom
CompileKotlin
task that gets the stdlib symbols from
android.jar
according to compileSdk (this is what I would expect)
c
…I guess I'll just continue using toolchains
maybe in the future I'll switch to compat-patrouille, but not tonight