Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Kotlin 2.0 #77

Open
SalomonBrys opened this issue May 30, 2024 · 3 comments
Open

Support Kotlin 2.0 #77

SalomonBrys opened this issue May 30, 2024 · 3 comments

Comments

@SalomonBrys
Copy link
Member

SalomonBrys commented May 30, 2024

This issue is here to discuss the path to supporting K2. It outlines the issues I've encountered as well as their possible workarounds. Feel free to enter the discussion, especially if you have any insight or wild idea ;)

MocKMP generates source code for the Test source-sets, which is not properly supported by KSP. KSP does support generating code for the commonMain sourceset, but not for the commonTest sourceset.

Up until now, we've used the following trick: register the MocKMP processor only in the kspJvmTest configuration and have the commonTest sourceset use the build/generated/ksp/jvm/jvmTest/kotlin directory. This requires some more Gradle configuration (namely having all targets compilation task to depend on kspTestKotlinJvm) but it worked allright (even though it made my heart ache).

Kotlin 2.0 brings a new issue:

e: Files '/[PROJECT]/build/generated/ksp/jvm/jvmTest/kotlin/tests/InjectionTests_injectMocks.kt', ... can be a part of only one module, but is listed as a source for both `jvmTest` and `commonTest`, please check you -Xfragment-sources options.

It looks like K2 is more restrictive than K1, and does not allow the same file to be in multiple sourcesets at once. I also haven't found a way to emulate running KSP to commonTest only, or to remove the generated/ksp/jvm/jvmTest/kotlin directory from the JVM test compilation.

I've tried another approach that consists of applying the processor to all test targets:

dependencies {
    configurations.names
        .filter { it.startsWith("ksp") && it.endsWith("Test") }
        .forEach {
            it(projects.mockmpProcessor)
        }
}

...but that brings a new issue: because K2 is also more restrictive as to which sources are accessed when compiling common code, this approach does not compile at all (yielding unresolved symbol all around errors when compiling common code).

So far, the only approach I've thought of that would (I think) resolve the issue is to follow the KotlinX.RPC & KotlinX.Serialization paths and have a Compiler plugin that adds an AssociatedObjectKey annotation to each class annotated with @Mock or @Fake. This cannot be done with KSP as it notoriously cannot modify the sources of the code it is processing. Such annotations allow a Native & Js code to access an object associated to a class at compile-time (it does not exist in JVM, but the JVM provides reflection so there's no need for it). This in turn allows to have a common code that does not directly reference generated code.
This is a path I'd rather not take, because:

  • It requires maintaining a compiler plugin for each Kotlin compiler version (KotlinX.RPC maintains so far 17 artifacts for each version, corresponding to 17 different Kotlin compiler version).
  • It requires maintaining code that uses undocumented and unsupported Kotlin compiler API, which can break at anytime.
  • It bundles MocKMP to each specific supported version of the Kotlin compiler and requires a level of reactivity and investment at each minor Kotlin update that I cannot provide as an Open-Source library author (without patron or revenue source related to my Open-Source work).

MocKMP is now at a crossroad as I have found, at the moment, no satisfactory path to have it compatible with K2.
Let's keep looking!
Make sure to share any idea or knowledge I've missed ;)

@GoodSir42
Copy link

Thanks for the detailed explanation of the problem. Even if I am pretty sure that I am only beginning to understand the basic setup maybe we can exchange a few thoughts here. If I understand the KSP docs here correctly there seems to be a kspTest task for multiplatform. Could we maybe hook into that? Also: Did you already investigate whether KSP 2.0 might or might not have an impact or helpful changes for the issue? I am struggling with finding specific information about it.

@SalomonBrys
Copy link
Member Author

There's no kspTest in a Kotlin/Multiplatform project.
Here are the available configurations on my mac:

kspCommonMainMetadata
kspIosSimulatorArm64
kspIosSimulatorArm64Test
kspIosX64
kspIosX64Test
kspJs
kspJsTest
kspJvm
kspJvmTest
kspLinuxX64
kspLinuxX64Test
kspMacosArm64
kspMacosArm64Test
kspMacosX64
kspMacosX64Test
kspMingwX64
kspMingwX64Test
kspTvosSimulatorArm64
kspTvosSimulatorArm64Test
kspTvosX64
kspTvosX64Test
kspWatchosSimulatorArm64
kspWatchosSimulatorArm64Test
kspWatchosX64
kspWatchosX64Test

As I've explained applying KSP to each test sourceset does not work because of K2 Common / Target sourceset separation.

@GoodSir42
Copy link

GoodSir42 commented Jun 1, 2024

Thanks for the explanation! So it looks like the issue here is really just that KSP runs for anything except commonTest if I see it well. Does this mean we could generate files for jvm test for example and run the tests there? I know it is not the goal of a multiplatform library to limit tests to the different sub packages, I am just trying to understand the underlying issue. In the meantime I will add my vote to the KSP issue and ask the ksp team to shed a light on why they seem to exclude commonTest explicitly.

Edit: would it be possible to move the generated code from one folder to the other in the generation step? I know this is hacky but it might keep the project running until the underlying issues in KSP are resolved/KSP is extended

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants