How can we create sub modules like we can easily d...
# multiplatform
a
How can we create sub modules like we can easily do on JVM (not multiplatform) projects? For instance, in the photo the module is recognized as a directory even though I have the include in my
settings.gradle.kts
Thanks
b
Because it overlaps with the path of another module sources. Move it to be a sibling of src and it will work
a
Thanks @Big Chungus, so I guess it's not possible to have nested sub modules like it's possible for normal jvm projects?
b
It is, it just can't be in the sources of another module
a
But the top level module is the KMP library (project name), So we can have MyLib which has src then inside it we have commonMain. You are saying we cannot have another nested module inside commonMain?
b
I.e. this is fine /rootProject /src /moduleA /moduleB /src /src /src This is not /rootProject /src /commonMain /kotlin /resources /moduleB
a
What if we want module B inside module A ? Is this possible in any ways?
b
See my first example. ModuleB path is /moduleA/moduleB
a
Ok trying this example right now
b
So it's inside moduleA. It would break only if you place in /moduleA/src/commonMain/moduleB since commonMain is a source dir of moduleA
Here's an example of nested modular mpp project https://github.com/mpetuska/klip
πŸ‘ 1
a
@Big Chungus Thanks a lot I got the hang of it now! I'd send you a coffee if I could!
b
Virtual coffee is fine. Always glad to help πŸ˜€
😁 1
Although I'd rename your commonMain module to something else as that's standard sourceSet name for mpp projects
Remember module != sourceSet
a
My initial thought was that everything included in this commonMain will not be jvm specific. How should I name it? It was named commonMain initially
b
Here's a reminder of gradle hierarchy Build -> Project -> sourceSet -> sourceRoot
It wasn't. sourceSet of a module was ;)
Name it shared, common or something like that
a
Is there a way to un-module it?
b
Is your repo open source?
I could raise a pr sorting this out
a
I plan on having a "core" module inside "commonMain". Do I just created this project, no code yet.
The idea is to have none-jvm specific implementations and code inside commonMain. commonMain -----core -----project-name-A -----project-name-B
b
Then have it named simply common
a
Ok and I guess it must be a module even though other sourceSet are not modules?
b
Otherwise you'll get it confused with sourceSet. e.g. commonMain/core/src/commonMain/kotlin/main.kt
a
πŸ‘Ž 1
b
All sourceSets are not modules. They're parts of a module
a
I see what you mean
It used to be sourceSet but now it isn't anymore and the sourceSet will be in the project module and in core. Got it (correct me if im wrong)
πŸ‘ 1
b
Have a look at this module (especially src subfolder structure) and then check how it's declared in settings.kts https://github.com/mpetuska/kon/tree/master/lib%2Fkon-core
πŸ‘ 1
a
Got it , I will create 2 modules then, one "common" where all its sub modules will have only a commonMain sourceSet and another module where each sub modules will have the sourceSet for js, native etc (but not common). would that be ok standard wise?
b
That's not even possible πŸ˜€
πŸ˜’ 1
You cannot have a mpp module without src/commonMain
Ok, let's try this - can you explain to me what modules you plan to have and their intended use ases?
a
Sure
b
I can then send you a file tree
a
Give me a minute, I'm making a folder structure
πŸ‘ 1
Here is what I want to achieve (I only created a minimal number of modules, but in reality each project will have like 5-6 project specific modules (one for each layers of the architecture), I did this to keep it simple for the explanation)
My goal is to have a Core module that can be used by the library and the jvm server module. Then inside the library I would like to have a project-based structure where I put project specific and library specific code. Context: The library will be used for offline users while the server will be a ktor server. Some of the code between the library and the server will be the same (this should be in core) but since the library and the server is 2 different application, I will put application business logic for the server inside the server module, application business rules for the library will be inside the library
My initial idea was to have this, but then I was not sure how we can make sure that core/ will work with the library and not use any jvm specific api.
I kept the image simple and didnt duplicate project-B in server but in theory there will always be project-B in both library and server.
b
Ok, here's the file tree up to kotlin sources for each module (modules with
commonMain
are MPP, with just
main
are JVM. each module also has gradle path to place in setting.kts listed)
Copy code
core/ # not a module
    entities/ # JVM module :core:entities
        src/
            main/kotlin
            test/kotlin
        build.gradle.kts
library/ #not a module
    project-A/ # MPP module :library:project-A
        build.gradle.kts
        src/
            commonMain/kotlin
            commonTest/kotlin
            jvmMain/kotlin
            jvmTest/kotlin
            jsMain/kotlin
            jsTest/kotlin
            linuxX64/kotlin
            linuxX64Test/kotlin
        library-specific-controllers/ # MPP module :library:project-A:library-specific-controllers
            build.gradle.kts
            src/
                commonMain/kotlin
                commonTest/kotlin
                jvmMain/kotlin
                jvmTest/kotlin
                jsMain/kotlin
                jsTest/kotlin
                linuxX64/kotlin
                linuxX64Test/kotlin
        library-specific-usecases/ # MPP module :library:project-A:library-specific-usecases
            build.gradle.kts
            src/
                commonMain/kotlin
                commonTest/kotlin
                jvmMain/kotlin
                jvmTest/kotlin
                jsMain/kotlin
                jsTest/kotlin
                linuxX64/kotlin
                linuxX64Test/kotlin
    project-B/ # MPP module :library:project-B
        build.gradle.kts
        src/
            commonMain/kotlin
            commonTest/kotlin
            jvmMain/kotlin
            jvmTest/kotlin
            jsMain/kotlin
            jsTest/kotlin
            linuxX64/kotlin
            linuxX64Test/kotlin
        library-specific-controllers/ # MPP module :library:project-B:library-specific-controllers
            build.gradle.kts
            src/
                commonMain/kotlin
                commonTest/kotlin
                jvmMain/kotlin
                jvmTest/kotlin
                jsMain/kotlin
                jsTest/kotlin
                linuxX64/kotlin
                linuxX64Test/kotlin
        library-specific-usecases/ # MPP module :library:project-B:library-specific-usecases
            build.gradle.kts
            src/
                commonMain/kotlin
                commonTest/kotlin
                jvmMain/kotlin
                jvmTest/kotlin
                jsMain/kotlin
                jsTest/kotlin
                linuxX64/kotlin
                linuxX64Test/kotlin
server/ # not a module
    project-A/ # JVM module :server:project-A
        server-specific-usecases/ # JVM module :server:project-A:server-specific-usecases
            src/
                main/kotlin
                test/kotlin
            build.gradle.kts
        src/
            main/kotlin
            test/kotlin
        build.gradle.kts
build.gradle.kts
settings.gradle.kts
πŸ‘ 1
note how modules are never in other module's
src
directory. Also the distinction between multiple sourceSets (even for JVM-only projects). I hope it now makes sense why the sourceSets are even needed.
To simplify, it might help to think of a module as a lego block. You cannot place another lego block inside a lego block, but uou can place them either side-by-side or on top of each other. Here's the bare minimum for such "lego block" that a module owns:
Copy code
entities/ # JVM module :core:entities
    src/
        main/kotlin
        test/kotlin
    build.gradle.kts
πŸ‘ 1
Or rather this to better fit my side-by-side analogy
Copy code
src/
    main/kotlin
    test/kotlin
build.gradle.kts
πŸ™ 1
a
Looks great, I have 2 questiosn. 1 : If
:core:entities
is a jvm module then will I be able to use it inside
library:project-B:library-specific-usecases
for instance (because that's the goal of core, to be used by the lib and the server) 2 : Can I delete the sourceSet
Copy code
jvmMain/kotlin
jvmTest/kotlin
jsMain/kotlin
jsTest/kotlin
linuxX64/kotlin
linuxX64Test/kotlin
inside
library:project-B:library-specific-usecases
since my use cases will be pure kotlin ?
Hmmmm actually it might be unsafe to do so, because what if my use case uses something very basic but that is jvm specific like a UUID, then I'd be in trouble. I guess it is safer to have the sourceSet. Beset case everything will be in commonMain but at least I wont be trapped. Is this correct?
Btw your lego block analogy is super clear
b
sourceSets are not required if you have no sources for them. ie. if you are able to write all your code in commonMain only, you can remove the others. Their use case is to build on top of commonMain (which does not have access to platform specific APIs) and be aable to access platform specific apis for that target
As for core, I just gave you a random shuffle of MPP vs JVM-only modules. You can change that by just switching kotlin gradle plugin from
kotlin("jvm")
to
kotlin("multiplatform")
in module's build.gradle.kts
πŸ‘ 1
Note that JVM module can depend on MPP module, but MPP module can only depend on JVM module for its jvmMain sourceSet
Also, sourceSet paths are implied by gradle.. You creating folders for them or not having them changes nothing πŸ˜„
a
Ok so I will be forced to have core an MPP module (with a jvm and a native sourceSet). Dumb question : Is it possible to have a MPP module but not publish the library externally? Since core is only used internally, would that be ok? So only referencing the MPP module from the library it via
:core:entities
for instance like we can for jvm modules for instance.
b
Sure, just add it as dependency to other MPP or JVM module. Here's how you add it to another MPP module
Copy code
kotlin {
    sourceSets {
        named("commonMain") {
            dependencies {
                implementation(project(":core:entities"))
            }
        }
    }
}
And for JVM it's even simpler
Copy code
dependencies {
    implementation(project(":core:entities"))
}
a
Perfect, then I think I'm all set! Thank you so much again you don't realize how this helps me !
b
I do. I started just as clueless when I first got into MPP πŸ˜„ It's a steep learning curve!
πŸ™ 1
πŸ™ 1
What helped me most actually is browsing through the setups of other OSS MPP projects.
πŸ’‘ 1
a
That is awesome, I will bookmark these for sure as the one you sent earlier already helped a lot