IncompatibleClassChangeError with Kotlin 2.0.20-Beta2 for native (iOS) targets
Which version of Kotest are you using Kotest 5.9.1
Hi!
We ran into an issue testing our KMP codebase against Kotlin 2.0.20-Beta2 (Beta1 works, 2.0.10-RC also works). Kotest fails to start the iOS tests due to an IncompatibleClassChangeError. Here's the output of out CI pipeline triggering it.
Thanks! Looks like a change in the Kotlin compiler that requires updating the Kotest compiler plugin.
e: java.lang.IncompatibleClassChangeError: Found class org.jetbrains.kotlin.ir.declarations.IrFactory, but interface was expected
at io.kotest.framework.multiplatform.embeddablecompiler.NativeTransformer.generateLauncher(NativeTransformer.kt:46)
at io.kotest.framework.multiplatform.embeddablecompiler.Transformer.visitModuleFragment(Transformer.kt:65)
at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitModuleFragment(IrElementTransformerVoid.kt:113)
at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitModuleFragment(IrElementTransformerVoid.kt:19)
at org.jetbrains.kotlin.ir.declarations.IrModuleFragment.accept(IrModuleFragment.kt:33)
at org.jetbrains.kotlin.ir.declarations.IrModuleFragment.transform(IrModuleFragment.kt:36)
at io.kotest.framework.multiplatform.embeddablecompiler.SpecIrGenerationExtension.generate(SpecIrGenerationExtension.kt:23)
at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.applyIrGenerationExtensions(convertToIr.kt:437)
at org.jetbrains.kotlin.fir.pipeline.Fir2IrPipeline.runActualizationPipeline(convertToIr.kt:246)
at org.jetbrains.kotlin.fir.pipeline.Fir2IrPipeline.convertToIrAndActualize(convertToIr.kt:130)
at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.convertToIrAndActualize(convertToIr.kt:99)
at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.convertToIrAndActualize$default(convertToIr.kt:72)
at org.jetbrains.kotlin.backend.konan.Fir2IrKt.fir2Ir(Fir2Ir.kt:99)
The latest snapshot might have this fixed @JesusMcCloud
it is indeed fixed with latest 6.0.0 SNAPSHOT. Thank you very much!
Still keeping this one open until 6.0.0 is released. Feel free to close at your own discretion.
Are there plans to backport the fix to 5.9.x?
Are there plans to backport the fix to 5.9.x?
We've just switched to using 6.0.0-SNAPSHOT. Works like a charm even on complex KMP projects!
Could it be possible to publish a version with this fix? Even if it's a 6.0.0-alpha.1 or similar, it doesn't have to be stable, I just can't depend on -SNAPSHOT versions because they are mutable.
I am also going to lend my voice to asking for a release for the fix for this breaking bug. Especially since kotest does not work with the latest stable version of Kotlin without it. I cannot use a constantly changing snapshot library to test production code.
In the meantime, we're using snapshot version 6.0.0-20240905.065253-61 as a test dependency. This version works for iOS, JVM, Android and is not mutable.
I apologize if I am missing something, but I am getting Failed to resolve: io.kotest:kotest-property:6.0.0-20240905.065253-61 errors. When I look on s01.oss.sonatype.org I see the usual structure is 6.0.0.(some number)-morenumbers. The highest snapshot in the directory is 6.0.0-1564 which still provides the original error since it predates the fix. Is there a better way to look through snapshots?
@alyssa-nash maybe you are not using a recent enough gradle version? if you look into the repo, it is there.
Gradle needs a special setup to access snapshot artifacts.
This is what I'm using to access the snapshots repo for the Kotest Gradle plugin:
In settings.gradle.kts:
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
pluginManagement {
repositories {
specialRepositories()
gradlePluginPortal()
}
}
repositories {
mavenCentral()
specialRepositories()
}
}
fun RepositoryHandler.specialRepositories() {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "MavenCentralSnapshots"
mavenContent { snapshotsOnly() }
}
}
In gradle/libs.versions.toml:
[versions]
# https://github.com/kotest/kotest/releases
io-kotest = "5.9.1"
io-kotest-gradle-plugin = "6.0.0-20240905.065253-61"
# https://github.com/JetBrains/kotlin/releases
org-jetbrains-kotlin = "2.0.20"
[libraries]
io-kotest-kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "io-kotest" }
io-kotest-kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "io-kotest" }
[plugins]
io-kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "io-kotest-gradle-plugin" }
org-jetbrains-kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "org-jetbrains-kotlin" }
In build.gradle.kts:
plugins {
alias(libs.plugins.org.jetbrains.kotlin.multiplatform)
alias(libs.plugins.io.kotest.multiplatform)
}
kotlin {
linuxX64 {
binaries.executable()
}
sourceSets {
val commonTest by getting {
dependencies {
implementation(libs.io.kotest.kotest.framework.engine)
implementation(libs.io.kotest.kotest.assertions.core)
}
}
}
}
Does that help?
I have never thought of NOT keeping the gradle plugin and the test dependencies in sync, but here it seems to be a viable strategy.
Thank you @OliverO2 and @JesusMcCloud. My issue was that some of the artifacts do not match. For instance kotest-property uses 6.0.0-20240905.065253-62, while some like kotest-framework-engine uses 6.0.0-20240905.065253-61. But I tried what @OliverO2 suggested and am using 5.9.1 for the test dependencies and keeping the gradle plugin to an immutable snapshot and it works like a charm.
Thanks for the feedback! Happy to hear that we have arrived at a viable solution.
Kotest 6.0 development is pretty dynamic with no release date in sight, so sticking to 5.9.1 for the library artifacts is best for stability, while the 6.0.0-xxx Gradle/compiler plugin supports the Kotlin 2.0.20 compiler. Best of both worlds, I'd say.
Unfortunately, new Kotlin versions can break the APIs used by compiler plugins at any time without advance notice. In this case, it was just a compiler class becoming an interface (a source-compatible binary incompatibility). This situation will be with us until the Kotlin compiler team figures out how to provide sufficiently stable compiler APIs.
This workaround doesn't seem to work for me.
Plugin [id: 'io.kotest.multiplatform', version: '6.0.0-20240905.065253-61'] was not found in any of the following sources:
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Included Builds (None of the included builds contain this plugin)
- Plugin Repositories (could not resolve plugin artifact 'io.kotest.multiplatform:io.kotest.multiplatform.gradle.plugin:6.0.0-20240905.065253-61')
Searched in the following repositories:
Gradle Central Plugin Repository
MavenCentralSnapshots(https://s01.oss.sonatype.org/content/repositories/snapshots/)
MavenRepo
And, indeed, there is no such version in the Central snapshots repository: https://s01.oss.sonatype.org/content/repositories/snapshots/io/kotest/multiplatform/io.kotest.multiplatform.gradle.plugin/
I can't find where that version is?
Edit. Ah, it's here. But why would Gradle search there?
I'd like to add my voice to those asking for a backported fix prior to the 6.0 release of the library. Setting up an artifactory remote to pull a snapshot is not an option at my company. This is currently blocking all Kotlin language fixes on KMP projects that use Kotest.
Can someone who managed to get the workaround working explain what they did? It doesn't work for me, as I explained in my previous message :/
I don't have it in an isolated project, but the combination of pinning a specific snapshot version for the plugin, alongside 5.9.1 for kotest dependencies works perfectly well in our conventions plugin. We simply set the kotest plugin version to a specific snapshot and everything else (like kotest-framework-engine, etc.) to 5.9.1.
https://github.com/wgpu4k/wgpu4k For this project, I've been using the latests v6 snapshots. So far, I haven't encountered any instability, although there's been a significant increase in download size with each new snapshot build.
https://github.com/wgpu4k/wgpu4k For this project, I've been using the latests v6 snapshots. So far, I haven't encountered any instability, although there's been a significant increase in download size with each new snapshot build.
yes, but this is always the latest snapshot. it is not a fixed buildrev.
I can't help with company policy, but for those still looking how to make this work: Have you tried to use the exact configuration I posted above?
@CLOVIS-AI: @OliverO2's solution is what the conventions plugin does, wrt. kotest.
I just remembered: we do have everything self-contained in one project as well. Here, we did exactly what @OliverO2 suggested, and it works like a charm! As far as I am concerned, he provided the solution.
I can also confirm this solution to be working for Kotlin 2.1.0-Beta1
6.0.0-20240905.065253-61 was just depublished. now it's broken again
would it be possible to have one snapshot build with this fix published more permanently or backport the fix to a 5.9.2 release? every time a snapshot gets depublished, the chore of updating every project starts anew.
I don't do releases, so I'm not the one to decide how to proceed. What I can tell is:
- This is not a "fix" that could be "backported". It is a recompilation of the Kotest compiler plugin.
- The current build structure uses the same Kotlin version for the library and the compiler plugin. So just updating the Kotlin version to have a fresh compiler plugin would force all Kotest users to use Kotlin 2.0.20 for the library by default. This may not work for everyone. Libraries are usually conservative and do not force newer dependencies upon users for a reason.
- Kotest does not currently have a release strategy in place which would maintain several version branches simultaneously. We work on master and publish a new release when it is ready.
Hopefully that helps to understand why resolving this case to everyone's satisfaction is not as straightforward as one might think. If we had unlimited resources, we'd be in a better position, of course.
We could publish a 6.0.0.M1 that would be permanent while making clear its prerelease
Please do! <3
I am unpinning the snapshot version in all of our projects, because pinning is no use anyway, given snapshots disappear within a week. So the idea, to depend on an immutable snapshot is good, but execution will never work for longer than a week.
I fully understand resources are limited, but again. just one 6.0.0 preview version of the gradle plugin would be highly appreciated!
@sksamuel Do you have new information on that pre-release version's availability? We have multiple repositories that are stuck with broken versions for more than two months now.