Hello, I tried using Koin with KSP, and I have no...
# amper
t
Hello, I tried using Koin with KSP, and I have not be able to generate code. I tried kotlin-inject, and I see code gets generated. However, I am not able to use the code generated in the common sources. Here what I tried
Copy code
product:
  type: lib
  platforms: [android, iosArm64, iosSimulatorArm64, iosX64]

dependencies:
  - io.insert-koin:koin-core:4.0.2
  - io.insert-koin:koin-annotations:2.0.0
  - me.tatarka.inject:kotlin-inject-runtime:0.8.0

settings:
  kotlin:
    ksp:
      processors:
        - io.insert-koin:koin-ksp-compiler:2.0.0
        - me.tatarka.inject:kotlin-inject-compiler-ksp:0.8.0
👀 1
And the code I tried
Copy code
package domain.di

import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Inject
import me.tatarka.inject.annotations.Provides
import org.koin.core.annotation.ComponentScan
import org.koin.core.annotation.Module

@Module
@ComponentScan
internal class SignalCollectorModule

// ---

@Component
abstract class AppComponent {
    abstract val repo: Repository

    protected val RealHttp.bind: Http
        @Provides get() = this
}

interface Http

@Inject
class RealHttp : Http

@Inject
class Api(private val http: Http)

@Inject
class Repository(private val api: Api)

fun test() {
    SignalCollectorModule().module
    AppComponent::class.create()
}
.module
is unresolved (and nothing generated about it. And
.create()
is not accessible in common. But is on android
Copy code
package domain.di

import kotlin.reflect.KClass

public fun KClass<AppComponent>.create(): AppComponent = InjectAppComponent()

public class InjectAppComponent : AppComponent() {
  override val repo: Repository
    get() = Repository(
      api = Api(
        http = RealHttp().bind
      )
    )
}
j
HI Tristan! Sorry it took so long to answer. This is a tricky situation. It has been brought up several times: https://github.com/google/ksp/issues/567 The main KSP contributor's answer is not really visible in the issue thread, but we had some spread out conversations about it. Essentially, this is because KSP follows the current Kotlin compilation model, in which there are only target compilations that each take a set of fragments to compile. When you compile for JVM, the Kotlin compiler expects `common`+`jvm`. When compiling for
iosArm64
, the compiler expects `common`+`native`+`apple`+`ios`+`iosArm64` sources. Etc. Each of these invocations produce target-specific code (JVM bytecode, native framework, etc.). In the KSP world it is very much mirrored. For each of those target compilations, KSP is called to process all sources. Any code generated by KSP at this point can only be placed in the most specific target fragment (e.g.
jvm
or
iosArm64
in the examples above), and thus cannot be available to common sources. In Gradle, people usually hack their way around this by adding an artificial dependency on common metadata compilation. This compilation is a special one that processes common code from the
common
fragment. So when we run a "common" KSP processing, it only gets common sources. If there is any codegen, it's ok to place the generated code in
common
as well, and thus it would make your generated declarations available. It would also create duplicate classes, because now we generate the same classes for
common
and for each target-specific compilation, which is why this is a hack. The best way will come with the new Kotlin compilation model where each fragment is compiled separately and we use the resulting klib of each fragment as a dependency of more specific fragments. This way KSP could run on each of those fragments, and the generated code, if any, can be safely placed in the corresponding fragment, and the natural visibility rules will work as expected. We're not there yet, though. As far as I understood, the "proper" way at the moment suggested by @laszio is to create
expect
declarations in
common
and make KSP generate the `actual`s in target compilations. This is really not ideal, though, and I can't really describe right now how it would look. Maybe Ting-Yuan can help here 🙂 I would love to implement a workaround for this in Amper, but I don't see a proper way to do so right now.
thank you color 1