Thread
#multiplatform
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Hello, is it possible to reference an interface (that has no impl) in a kotlin MPP library commonMain from a different Java/Kotlin only Module? myJavaModule -> MPP Library/commonMain/MyInterface Thanks
    Javier

    Javier

    9 months ago
    if the kmp project has jvm target, yes
    Alexandre Brown

    Alexandre Brown

    9 months ago
    It has, but I'm not sure I understand how to do it because it is not working
    The Core (KMP library)
    The Kotlin/Jvm Module (it has references to the Core/UseCases module)
    Yet inside the Kotlin/Jvm module I cannot reference the interfaces...
    @Javier Here is a better picture of the structure. I would like
    MyUseCaseImpl.kt
    to be able to implement the
    UseCaseInterface.kt
    Javier

    Javier

    9 months ago
    you can reference it
    Alexandre Brown

    Alexandre Brown

    9 months ago
    What do you mean exactly? Could you elaborate? It does not let me reference it (see photo above).
    @Big Chungus Hey if you have an idea by any chance I'd love your feedback.
    Big Chungus

    Big Chungus

    9 months ago
    Let me have a look at UseCaseInterface, MyUseCaseImpl and dependencies of root-clasification (in build.gradle.kts)
    Alexandre Brown

    Alexandre Brown

    9 months ago
    ok give me a moment
    @Big Chungus Hopefully this helps
    Big Chungus

    Big Chungus

    9 months ago
    add this to the top of
    MyUseCaseImpl
    import UseCaseInterface
    Even though you have no package declared, it still needs to be imported
    There's no way to add things to global context like stdlib
    Alexandre Brown

    Alexandre Brown

    9 months ago
    But we cannot create packages in non-jvm environment no?
    I tried that and simply having the import you suggested but it does not work. It keeps suggesting me to add :core:usecases:commonMain as a dependency
    It seems like the issue can be boiled down to "referencing a KMP library from a jvm only module"
    Big Chungus

    Big Chungus

    9 months ago
    Shouldn't be an issue, really. Can you send me the content of build.gradle.kts of both modules?
    Alexandre Brown

    Alexandre Brown

    9 months ago
    backend/road-classification/usecases/build.gradle.kts
    dependencies {
    	implementation(project(":core:entities"))
    	implementation(project(":core:usecases"))
    }
    backend/road-classification/build.gradle.kts
    EMPTY
    backend/build.gradle.kts
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    val testImplementation by configurations
    val implementation by configurations
    
    val kotlinxCoroutinesVersion: String by project
    val kotlinxDatetimeVersion: String by project
    val kotestVersion: String by project
    val mockkVersion: String by project
    val logbackVersion: String by project
    val kodeinVersion: String by project
    val ktorVersion: String by project
    
    val entryPointPath = "MainKt"
    
    plugins {
    	application
    	kotlin("jvm") version "1.6.0" apply false
    	kotlin("plugin.serialization") version "1.6.0" apply false
    }
    
    application {
    	mainClass.set(entryPointPath)
    }
    
    repositories {
    	maven {
    		url = uri("<https://maven.pkg.jetbrains.space/public/p/ktor/eap>")
    	}
    }
    
    dependencies {
    	project(":backend:road-classification").dependencyProject.allprojects.forEach(::implementation)
    
    	implementation("io.ktor:ktor-serialization:$ktorVersion")
    	implementation("io.ktor:ktor-server-core:$ktorVersion")
    	implementation("io.ktor:ktor-server-cio:$ktorVersion")
    	implementation("io.ktor:ktor-server-locations:$ktorVersion")
    	implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
    	implementation("io.ktor:ktor-server-websockets:$ktorVersion")
    	implementation("ch.qos.logback:logback-classic:$logbackVersion")
    	implementation("org.kodein.di:kodein-di-framework-ktor-server-jvm:$kodeinVersion")
    
    	testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
    }
    
    tasks.withType<KotlinCompile> {
    	kotlinOptions.freeCompilerArgs += listOf("-Xopt-in=io.ktor.server.locations.KtorExperimentalLocationsAPI")
    }
    
    allprojects {
    
    	apply {
    		plugin("org.jetbrains.kotlin.jvm")
    	}
    
    	tasks.withType<KotlinCompile> {
    		kotlinOptions.jvmTarget = "11"
    	}
    
    	dependencies {
    		implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetimeVersion")
    		implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")
    
    		testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
    		testImplementation("io.kotest:kotest-property:$kotestVersion")
    		testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")
    		testImplementation("io.mockk:mockk:$mockkVersion")
    	}
    
    	tasks.withType<Test> {
    		useJUnitPlatform()
    	}
    }
    core/usecases/build.gradle.kts
    EMPTY
    core/build.gradle.kts
    import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
    
    plugins {
    	kotlin("multiplatform") version "1.6.0" apply false
    }
    
    subprojects {
    	apply {
    		plugin("org.jetbrains.kotlin.multiplatform")
    	}
    
    	extensions.getByType<KotlinMultiplatformExtension>().apply {
    		/* Targets configuration omitted.
    		*  To find out how to configure the targets, please follow the link:
    		*  <https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets> */
    
    		jvm {
    			compilations.all {
    				kotlinOptions.jvmTarget = "11"
    			}
    			withJava()
    			testRuns["test"].executionTask.configure {
    				useJUnitPlatform()
    			}
    		}
    		val hostOs = System.getProperty("os.name")
    		val isMingwX64 = hostOs.startsWith("Windows")
    		val nativeTarget = when {
    			hostOs == "Mac OS X" -> macosX64("native")
    			hostOs == "Linux" -> linuxX64("native")
    			isMingwX64 -> mingwX64("native")
    			else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    		}
    
    		sourceSets {
    			val commonMain by getting {
    				dependencies {
    					implementation(kotlin("stdlib-common"))
    				}
    			}
    			val commonTest by getting {
    				dependencies {
    					implementation(kotlin("test-common"))
    					implementation(kotlin("test-annotations-common"))
    				}
    			}
    			val jvmMain by getting
    			val jvmTest by getting
    			val nativeMain by getting
    			val nativeTest by getting
    		}
    	}
    }
    Big Chungus

    Big Chungus

    9 months ago
    Ok, so turns out you are not actually applying kotlin plugin to any of the modules
    in your allprojects blocks, you have this
    apply {
    		plugin("org.jetbrains.kotlin.jvm")
    	}
    Which does absolutely nothing To correctly apply plugins you need this
    apply(plugin = "plugin.id")
    Which in turn explains why your subprojects are not able to resolve correct kotlin artefacts via gradle metadata
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Ok wow let me try!
    Hmm not sure If I applied it correctly
    import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
    
    plugins {
    	kotlin("multiplatform") version "1.6.0" apply false
    }
    
    subprojects {
    	apply(plugin = "org.jetbrains.kotlin.multiplatform")
    
    	extensions.getByType<KotlinMultiplatformExtension>().apply {
    		/* Targets configuration omitted.
    		*  To find out how to configure the targets, please follow the link:
    		*  <https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets> */
    
    		jvm {
    			compilations.all {
    				kotlinOptions.jvmTarget = "11"
    			}
    			withJava()
    			testRuns["test"].executionTask.configure {
    				useJUnitPlatform()
    			}
    		}
    		val hostOs = System.getProperty("os.name")
    		val isMingwX64 = hostOs.startsWith("Windows")
    		val nativeTarget = when {
    			hostOs == "Mac OS X" -> macosX64("native")
    			hostOs == "Linux" -> linuxX64("native")
    			isMingwX64 -> mingwX64("native")
    			else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    		}
    
    		sourceSets {
    			val commonMain by getting {
    				dependencies {
    					implementation(kotlin("stdlib-common"))
    				}
    			}
    			val commonTest by getting {
    				dependencies {
    					implementation(kotlin("test-common"))
    					implementation(kotlin("test-annotations-common"))
    				}
    			}
    			val jvmMain by getting
    			val jvmTest by getting
    			val nativeMain by getting
    			val nativeTest by getting
    		}
    	}
    }
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    val testImplementation by configurations
    val implementation by configurations
    
    val kotlinxCoroutinesVersion: String by project
    val kotlinxDatetimeVersion: String by project
    val kotestVersion: String by project
    val mockkVersion: String by project
    val logbackVersion: String by project
    val kodeinVersion: String by project
    val ktorVersion: String by project
    
    val entryPointPath = "MainKt"
    
    plugins {
    	application
    	kotlin("jvm") version "1.6.0" apply false
    	kotlin("plugin.serialization") version "1.6.0" apply false
    }
    
    application {
    	mainClass.set(entryPointPath)
    }
    
    repositories {
    	maven {
    		url = uri("<https://maven.pkg.jetbrains.space/public/p/ktor/eap>")
    	}
    }
    
    dependencies {
    	project(":backend:road-classification").dependencyProject.allprojects.forEach(::implementation)
    
    	implementation("io.ktor:ktor-serialization:$ktorVersion")
    	implementation("io.ktor:ktor-server-core:$ktorVersion")
    	implementation("io.ktor:ktor-server-cio:$ktorVersion")
    	implementation("io.ktor:ktor-server-locations:$ktorVersion")
    	implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
    	implementation("io.ktor:ktor-server-websockets:$ktorVersion")
    	implementation("ch.qos.logback:logback-classic:$logbackVersion")
    	implementation("org.kodein.di:kodein-di-framework-ktor-server-jvm:$kodeinVersion")
    
    	testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
    }
    
    tasks.withType<KotlinCompile> {
    	kotlinOptions.freeCompilerArgs += listOf("-Xopt-in=io.ktor.server.locations.KtorExperimentalLocationsAPI")
    }
    
    allprojects {
    
    	apply(plugin = "org.jetbrains.kotlin.jvm")
    
    	tasks.withType<KotlinCompile> {
    		kotlinOptions.jvmTarget = "11"
    	}
    
    	dependencies {
    		implementation("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetimeVersion")
    		implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")
    
    		testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
    		testImplementation("io.kotest:kotest-property:$kotestVersion")
    		testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")
    		testImplementation("io.mockk:mockk:$mockkVersion")
    	}
    
    	tasks.withType<Test> {
    		useJUnitPlatform()
    	}
    }
    Does not seem to solve the issue 😕
    Big Chungus

    Big Chungus

    9 months ago
    send me build file of the module where you want to have core as a dependency. In the ones above i don't see it added as a dependency
    Alexandre Brown

    Alexandre Brown

    9 months ago
    dependencies {
    	implementation(project(":core:entities"))
    	implementation(project(":core:usecases"))
    }
    Big Chungus

    Big Chungus

    9 months ago
    That's not the buildfile and I cannot see them in either of the above snippets of buildfiles
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Here is the entire project, it's pretty new so I can send it directly if that's better
    Javier

    Javier

    9 months ago
    you can set packages to non jvm targets
    Big Chungus

    Big Chungus

    9 months ago
    Is it public on github by any chance?
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Here I sent the zip, does it show up on slack ?
    Big Chungus

    Big Chungus

    9 months ago
    GH is just easier to work with 😄
    Looking at the zip now
    Alexandre Brown

    Alexandre Brown

    9 months ago
    We use gitlab private repos but I can upload it to GH since it's fresh if that's easier for you
    Big Chungus

    Big Chungus

    9 months ago
    Nah, zip will do
    Here's your issue
    Circular dependency between the following tasks:
    :backend:road-classification:usecases:classes
    \--- :backend:road-classification:usecases:compileJava
         +--- :backend:road-classification:usecases:compileKotlin
         |    \--- :backend:road-classification:usecases:jar
         |         +--- :backend:road-classification:usecases:classes (*)
         |         +--- :backend:road-classification:usecases:compileKotlin (*)
         |         \--- :backend:road-classification:usecases:inspectClassesForKotlinIC
         |              +--- :backend:road-classification:usecases:classes (*)
         |              \--- :backend:road-classification:usecases:compileKotlin (*)
         \--- :backend:road-classification:usecases:jar (*)
    Alexandre Brown

    Alexandre Brown

    9 months ago
    hmmmm
    backend:road-classification:usecases
    references core which references nobody...
    Big Chungus

    Big Chungus

    9 months ago
    There's something leaking through somewhere in your many allprojects and subprrojects blocks. One of the reasons why those are not recommended to use.
    Gotta go now, but I'll try to help tomorrow if you cannot figure it out by then.
    Alexandre Brown

    Alexandre Brown

    9 months ago
    I didnt know it was bad practice, should I duplicate the code inside each sub module's build.gradle.kts instead?
    Big Chungus

    Big Chungus

    9 months ago
    Not necessarily. You can use precompiled plugins or convention plugins to share logic (have a look at buildSrc in my projects)
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Ok will do, thanks again!
    @Big Chungus I just fixed the issue. It was very sneaky. I had this in the root
    build.gradle.kts
    subprojects {
        group = "com.mdai"
        version = "1.0.0"
        
        repositories {
            mavenCentral()
        }
    }
    The fact that all sub projects had the same group caused a name clash. Moving this out of the root
    build.gradle.kts
    group = "com.mdai"
    version = "1.0.0"
    And putting it inside the
    core/build.gradle.kts
    and inside
    backend/build.gradle.kts
    and of course making sure they both have unique group names fixed my issue. Eg:
    backend build.gradle.kts
    :
    subprojects {
    
    	group = "com.mdai.backend"
    	version = "1.0.0"
    core build.gradle.kts
    :
    subprojects {
    
    	group = "com.mdai.core"
    	version = "1.0.0"
    It actually didn't make sense to have a single group name for multiple apps/libs, I suspect this was a leftover from the initial project setup 🤦 Thanks again for your help!
    Big Chungus

    Big Chungus

    9 months ago
    I'm glad it works, but group actually should be the same
    This way it "groups" related artefacts together. Your issue was non-uniqye module names
    e.g. core:usecases and backend:usecases both produce identically named artefacts
    Alexandre Brown

    Alexandre Brown

    9 months ago
    The thing is,
    backend
    ,
    core
    and
    library
    could all be in separate repos if I wanted to. For simplicity I put them in the same repo since they both share
    core
    . But I see
    backend
    as being an artifact,
    core
    as being another one and
    library
    as another. They can all be deployed independently.
    But yes if we keep the same group name then the clash comes from the same module name. This is how I discovered it, I tried creating an interface in another module of
    core
    (eg:
    core/entities
    ) and I was able to reference it indeed. This is how I figured out it had to do with a name clash.
    But maybe my understanding of what
    group
    represents is not right. What do you think? I could also keep the same group and have the module named differently like
    core/core-usecases
    instead of
    core/usecases
    Big Chungus

    Big Chungus

    9 months ago
    Well maven artefact is defined by group + name Knowing that, you need to ensure that all modules under the same group have unique names (not just paths)
    I think for you it would make most sense to have three subgroups containing multiple modules com.mdai.backend com.mdai.core com.mdai.library The modules in each of the subgroup would share the group, but have unique names within that group.
    Which is close to where you're at already
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Yes this is pretty much what I ended up having ! 🙂
    Thanks for sharing your thoughts, it really helped
    Big Chungus

    Big Chungus

    9 months ago
    I'm glad.
    I actually got stuck on this exact same issue with unique artefact coordinates once before, but it was so long ago that I've managed to forget all about it 😀
    Alexandre Brown

    Alexandre Brown

    9 months ago
    Haha wow, not too surprising, it's a very sneaky error!
    But of course we forget about those 😆.