Hi friends! I stand before you and beseech your wi...
# multiplatform
p
Hi friends! I stand before you and beseech your wisdom yet again. 🙂 The majority of our tooling is written in Kotlin, and one of my goals is to gradually migrate the eligible projects to Kotlin 2.0 (and maybe decouple from JVM and go multiplatform). I apologise in advance if this sounds as if I am not doing my homework, but trying to rewrite our existing projects to KMP 2.0 is posing more challenges than I originally imagined (or maybe I even might've overlooked the exiting documentation). This is the project currently giving me some headaches: it's basically a web server that provides a UI that queries package documentation (you can see it live here). It's a multiplatform project composed of basically three subprojects: •
server/
→ the server itself and the main entry point. It's JVM. •
shared/
→ as the name implies, it has common code that's shared between the other two. •
client/
→ written in Kotlin/JS, provides the UI things. I tried to simplify all
build.gradle.kts
files, but failed miserably. Desperate times call for desperate measures, so I asked JetBrains AI to generate me a
build.gradle.kts
based on a prompt similar to the above description. This is what I got: code in thread 🧵 I was a bit confused on this proposal, mostly because it doesn't seem to match my findings (there's a high chance I am in the wrong here). So here I am poking the Jedis here in the hopes of shedding some light into this migration path that's currently a bit obscure for me. 🙂 Any suggestions are welcome! Thanks!
Code generated by JetBrains AI assistant:
Copy code
plugins {
    kotlin("multiplatform") version "<version>"
}

group = "<YourGroup>"
version = "<YourVersion>"

repositories {
    mavenCentral()
}

kotlin {
    jvm("server") {
        compilations["main"].defaultSourceSet {
            dependencies {
                implementation("io.ktor:ktor-server-core:<version>")
                implementation("io.ktor:ktor-server-netty:<version>")
                implementation(project(":shared"))
            }
        }

        binaries {
            executable {
                entryPoint = "<MainClassNameHere>"
            }
        }
    }

    js("client") {
        browser {
            commonWebpackConfig {
                cssSupport.enabled = true
            }
        }

        compilations["main"].defaultSourceSet {
            dependencies {
                implementation(project(":shared"))
            }
        }
    }

    sourceSets {
        val sharedMain by getting {
            dependencies {
                implementation(kotlin("stdlib-common"))
            }
        }
        val sharedTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }
        val jvmMain by getting {
            dependencies {
                implementation(kotlin("stdlib-jdk8"))
            }
        }
        val jvmTest by getting {
            dependencies {
                implementation(kotlin("test"))
                implementation(kotlin("test-junit"))
            }
        }
        val jsMain by getting {
            dependencies {
                implementation(kotlin("stdlib-js"))
            }
        }
        val jsTest by getting {
            dependencies {
                implementation(kotlin("test-js"))
            }
        }
    }
}
Caveat emptor: I didn't try it.
r
There's essentially two ways you can set this up: 1. A single Gradle sub-project, with source sets for shared (common), server (jvm), and client (js). This looks like what the AI made. 2. A sub-project for shared (with common, js, and jvm source sets), a jvm-only sub-project for server which depends on shared in the usual way (i.e.
implementation(project(":shared"))
), and a js-only (although it's still multiplatform) sub-project for client that also depends on shared.
shared
would define any `actual`'s it needs, client and server just treat it like a normal dependency. I'd recommend 2 over 1 because it keeps the project structure matching conceptual structure, and potentially lets you use shared elsewhere. 1 would imply that the "js facet" of shared is actually the client, which doesn't sound right.
gratitude thank you 1
p
Thanks so much @rnett! I will do some experiments around and report back. Cheers! gratitude thank you
a
@Paulo Cereda If you ignore the java code, this works perfectly fine: https://github.com/audax/kotlin-mpp-java-type-error
gratitude thank you 1
The sub project variant is much nicer imho.
I include the JS module with typescript definitions into my react project by just installing it into the NPM workspace. It's not yet integrated into the build however and in the repo there is no react project 😄
😉 1
p
Thanks @audax, it looks great! gratitude thank you
❤️ 1
Yay, I am making progress! I think I managed to make at least the server run (of course, I still need to figure out other parts of our project, but those can come later). I used @audax's sample project as base. I just have a small question: is there any reason to have
kotlin("jvm")
+
application
in the
server/
subproject? I thought I should go with
kotlin("multiplatform")
everywhere and then configure it through
jvm { ... }
or similar (still could not make it work, though). Thanks!
a
Using just "multiplatform" works as well, but it's less compatible with other Gradle plugins, while kotlin("jvm") is in most cases a drop in replacement for the Java plugin. Converting a project to a multiplatform one later is very simple, so starting with just the jvm part won't hurt you in the long run.
gratitude thank you 1