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

Multiplatform example in IntelliJ IDEA: "Unresolved reference: Foo" #963

Open
OliverO2 opened this issue Apr 20, 2022 · 13 comments
Open

Multiplatform example in IntelliJ IDEA: "Unresolved reference: Foo" #963

OliverO2 opened this issue Apr 20, 2022 · 13 comments
Labels
bug Something isn't working

Comments

@OliverO2
Copy link

I have experimented with KSP's examples/multiplatform with IntelliJ IDEA 2022.1, commenting out androidNative* and mingwX64 targets in examples/multiplatform/workload/build.gradle.kts (sections kotlin and dependencies).

Being unaware of generated files, the IDE complains about examples/multiplatform/workload/src/linuxX64Main/kotlin/Main.kt:

Unresolved reference: Foo

This can be fixed by adding the following to kotlin.sourceSets:

        val commonMain by getting {
            kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
        }

However, then executing gradlew :workload:clean :workload:allTests produces Gradle warnings and compilation errors (excerpt):

> Task :workload:kspKotlinJs FAILED
e: Source file or directory not found: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin/Foo.kt
> Task :workload:kspKotlinJvm FAILED
e: Source file or directory not found: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin/Foo.kt
> Task :workload:kspCommonMainKotlinMetadata
Execution optimizations have been disabled for task ':workload:kspCommonMainKotlinMetadata' to ensure correctness due to the following reasons:
  - Gradle detected a problem with the following location: '/home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain'. Reason: Task ':workload:kspKotlinJs' uses this output of task ':workload:kspCommonMainKotlinMetadata' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
  - Gradle detected a problem with the following location: '/home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain'. Reason: Task ':workload:kspKotlinJvm' uses this output of task ':workload:kspCommonMainKotlinMetadata' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
  - Gradle detected a problem with the following location: '/home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin'. Reason: Task ':workload:kspKotlinJs' uses this output of task ':workload:kspCommonMainKotlinMetadata' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
  - Gradle detected a problem with the following location: '/home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin'. Reason: Task ':workload:kspKotlinJvm' uses this output of task ':workload:kspCommonMainKotlinMetadata' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
w: [ksp] [Bar.kt, Baz.kt]
w: [ksp] [Bar.kt, Baz.kt, Foo.kt]
> Task :workload:kspKotlinLinuxX64
Execution optimizations have been disabled for task ':workload:kspKotlinLinuxX64' to ensure correctness due to the following reasons:
  - Gradle detected a problem with the following location: '/home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin'. Reason: Task ':workload:kspKotlinLinuxX64' uses this output of task ':workload:kspCommonMainKotlinMetadata' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.
w: [ksp] [Bar.kt, Baz.kt, Main.kt]
w: [ksp] [Bar.kt, Baz.kt, Main.kt, Foo.kt]
FAILURE: Build completed with 2 failures.

NOTE: Trying to fix the Gradle warnings with the idea plugin's module configuration as suggested in KSP quickstart appears to have no effect with multiplatform.

The Gradle warnings can be fixed by adding the required dependencies:

afterEvaluate {  // WORKAROUND: both register() and named() fail – https://github.com/gradle/gradle/issues/9331
    tasks {
        withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
            if (name != "kspCommonMainKotlinMetadata")
                dependsOn("kspCommonMainKotlinMetadata")
        }
    }
}

Now, executing gradlew :workload:clean :workload:allTests proceeds without the Gradle warnings, but still exhibits compilation errors (excerpt):

> Task :workload:compileKotlinLinuxX64 FAILED
e: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/linuxX64/linuxX64Main/kotlin/Foo.kt: (3, 7): Redeclaration: Foo
e: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin/Foo.kt: (3, 7): Redeclaration: Foo
> Task :workload:compileKotlinJs FAILED
e: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/js/jsMain/kotlin/Foo.kt: (3, 7): Redeclaration: Foo
e: /home/oliver/Repositories/experimental.nobackup/ksp/examples/multiplatform/workload/build/generated/ksp/metadata/commonMain/kotlin/Foo.kt: (3, 7): Redeclaration: Foo
FAILURE: Build completed with 2 failures.

This can be fixed by making KSP stop generating the same class multiple times (in commonMain as well as per target), reducing the dependencies section to:

dependencies {
    add("kspCommonMainMetadata", project(":test-processor"))
}

Now gradlew :workload:clean :workload:allTests completes successfully. And IntelliJ IDEA is happy as well.

Could this be integrated into the example and documentation be added to KSP with Kotlin Multiplatform?

@dilraj-singh1997
Copy link

dilraj-singh1997 commented Apr 28, 2022

If anyone looking for the same error in android studio, check this once- #37 (comment).
I guess the same principle can be extended for KMM.

@flaringapp
Copy link

flaringapp commented Mar 27, 2023

This issue is still there. Couple of clarifications though:

  1. 'Workaround' compiles fine without afterEvaluate
  2. KotlinCompile task is responsible for compiling JVM only. If you compile iOS as well, consider adding the same 'workaround' with KotlinNativeCompile class. I bet the same thing can be done for other platforms.
  3. Removing ksp processor from all configurations except kspCommonMainMetadata means that no code will be generated from shared module source sets except common. E.g., in KMM project androidMain and iosMain won't be able to generate any code.

@bcmedeiros
Copy link

bcmedeiros commented Sep 4, 2023

If all you care about is generating code for the commonMain source set, You can do the following:

plugins {
    kotlin("multiplatform")
    id("com.google.devtools.ksp")
}

kotlin {
    linuxArm64()
    linuxX64()
    macosArm64()
    macosX64()
    jvm()
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    // ...

    sourceSets {
        val commonMain by getting {
            kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
            // ...
        }
        val commonTest by getting {
            // ...
        }
    }

    // following example here:
    // https://github.com/OliverO2/kotlin-multiplatform-ksp/blob/main/base/build.gradle.kts
    dependencies {
        // Provide symbol processing for each Kotlin '*Main' source set.
        kotlin.sourceSets.forEach { sourceSet ->
            val kspConfiguration = when {
                sourceSet.name == "commonMain" -> "kspCommonMainMetadata"
                // but skip configurations for each platform-specific source set
                // sourceSet.name.endsWith("Main") -> "ksp${sourceSet.name.substringBefore("Main").replaceFirstChar { it.titlecase() }}"
                else -> null
            }
            if (kspConfiguration != null) add(kspConfiguration, project(":gameplay-protocol-converter-processor"))
        }
    }
}

// Fix KSP task dependencies (https://github.com/google/ksp/issues/963)
afterEvaluate {
    tasks {
        val kspCommonMainKotlinMetadata by getting
        withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
            if (this !== kspCommonMainKotlinMetadata) {
                dependsOn(kspCommonMainKotlinMetadata)
            }
        }
    }
}

This makes all platforms work and IntelliJ recognizes all the generated source files.

@bcmedeiros
Copy link

bcmedeiros commented Sep 7, 2023

This issue is really bizarre... I just updated to Kotlin 1.9.10, and Gradle started saying that the task kspCommonMainKotlinMetadata didn't exist, but if you println all the tasks names, it obviously exists. I had to change my afterEvaluate to the below, but I have no idea why it works, nor how safe this is.

afterEvaluate {
    val compileTasks = CopyOnWriteArrayList<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>()
    tasks {
        withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
            if (name != "kspCommonMainKotlinMetadata") {
                compileTasks.add(this)
            } else {
                compileTasks.forEach { t ->
                    t.dependsOn(this)
                }
                compileTasks.clear()
            }
        }
    }
}

@supenkwibowo
Copy link

I also got the same problem after updating to Kotlin 1.9.10. The interesting thing is that while trying to get kspCommonMainKotlinMetadata in afterEvaluate is not working, trying to do it after all projects evaluated in gradle.projectsEvaluated works.

gradle.projectsEvaluated {
    tasks {
        val kspCommonMainKotlinMetadata by getting
        withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
            if (this !== kspCommonMainKotlinMetadata) {
                dependsOn(kspCommonMainKotlinMetadata)
            }
        }
    }
}

@ting-yuan ting-yuan added the bug Something isn't working label Oct 26, 2023
@danilla
Copy link

danilla commented Nov 13, 2023

What I don't quite understand is if there is planned support for commonMain source set in IntelliJ idea out of the box. I mean, in the same way as currently e.g. jvmMain and jsMain source sets are supported.

In this issue, various gradle tricks are described which allow to reference generated code from user's common source sets. Then, there is advice on how to fix things when those tricks no longer work in newer versions.

But is it planned that generated code can be referenced from common source sets out of the box? Maybe it's not in this but in another issue?
Thanks!

@OliverO2
Copy link
Author

I haven't tracked KSP for a while, but once upon a time I had collected all sorts of interconnected issues regarding source set support in KSP. It did not go well... Maybe you could start here to get an impression: #1021 (comment)

@Lysander
Copy link

We have found a quite simple solution for all the trouble with KSP and Multiplatform-Projects (including publishing - which started to break the known workarounds with gradle 8.x):

// KSP support for Lens generation - our KSP based Code-Generator, replace with your own!
dependencies.kspCommonMainMetadata(project(":lenses-annotation-processor"))

// solves all implicit dependency trouble and IDEs source code detection
kotlin.sourceSets.commonMain { tasks.withType<KspTaskMetadata> { kotlin.srcDir(destinationDirectory) } }

As mentioned in the gradle docs, which are presented on such missing dependencies, a property carries dependency information, whereas simple path-Strings do not!

All trouble with build and publish (!) tasks simply went away - should be added to the documentation of KSP imho!

I hope this helps someone who also get into such trouble as we did...

@jamesrapadmi
Copy link

I have this issue as well. None of the workarounds in this thread work for me so far

@Lysander
Copy link

I have this issue as well. None of the workarounds in this thread work for me so far

Which Kotlin and Gradle version do you use?

I have tested my proposal not yet with 2.0! Might be possible some things have changed there...

@jamesrapadmi
Copy link

Yeah this is with kotlin 2.0 and Gradle 8.7

@bcmedeiros
Copy link

bcmedeiros commented Aug 17, 2024

// KSP support for Lens generation - our KSP based Code-Generator, replace with your own!
dependencies.kspCommonMainMetadata(project(":lenses-annotation-processor"))

// solves all implicit dependency trouble and IDEs source code detection
kotlin.sourceSets.commonMain { tasks.withType<KspTaskMetadata> { kotlin.srcDir(destinationDirectory) } }

This is good stuff, @Lysander, thanks for that. I'm posting my version below which is slightly different in terms of positioning the 2 pieces of config, maybe it will help someone.

import com.google.devtools.ksp.gradle.KspTaskMetadata

plugins {
    kotlin("multiplatform")
    id("multiplatform-module-standard")
    id("com.google.devtools.ksp")
}

kotlin {
    linuxArm64()
    linuxX64()
    macosArm64()
    macosX64()

    sourceSets {
        commonMain {
            // (2) above
            tasks.withType<KspTaskMetadata> { kotlin.srcDir(destinationDirectory) }
            dependencies {
                api(project(":gameplay-protocol-converter-annotation"))
                api(project(":domain"))
                api(project(":gameplay-protocol"))
            }
        }
        commonTest {
            dependencies {
                implementation(kotlin("test"))
            }
        }
    }
}

// (1) above
dependencies {
    kspCommonMainMetadata(project(":gameplay-protocol-converter-processor"))
}

@udev
Copy link

udev commented Aug 19, 2024

@bcmedeiros Thank you for this. I've been struggling to migrate a project to k2 and this helped with figuring out Koin ksp generation. Not sure if their instructions are just out of date but what you provided works perfectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

10 participants