Nova shema odvojene kompilacije u Kotlinu dovodi multiplatformske buildove u sklad s IDE-om

  • PublicPublic
  • 28 Sep, 2026
Nova shema odvojene kompilacije u Kotlinu dovodi multiplatformske buildove u sklad s IDE-om

Postojeći pristup kompilaciji Kotlin Multiplatforma radi, ali može proizvesti neočekivano i teško predvidivo ponašanje. Srž problema je u neslaganju između IDE-a i prevoditelja: oni različito razrješavaju pozive iz zajedničkih skupova izvornog koda prema deklaracijama iz ovisnosti. U Kotlinu 2.5.0-Beta1 JetBrains je dodao opcionalnu „odvojenu kompilaciju", koja to treba ispraviti — dovesti rezultat kompilacije u sklad s analizom u IDE-u i preciznije ukazivati na problematične pozive iz zajedničkih skupova izvornog koda u kod biblioteka. Kao bonus, shema također omogućuje inkrementalnu kompilaciju za zajedničke skupove izvornog koda.

Problem vuče korijene iz strukture KMP modula. Modul sa zajedničkim kodom obično sadrži nekoliko skupova izvornog koda: zajednički (commonMain) i skupove za pojedine platforme (jvmMain za Kotlin/JVM). Tijekom kompilacije platformskog skupa izvornog koda, kod u jvmMainu plus svi povezani zajednički skupovi kompilira se protiv platformskih artefakata ovisnosti. Zbog toga kod u commonMainu dobiva mogućnost implicitnog razrješavanja prema deklaraciji iz jvmMaina u ovisnosti — a upravo to IntelliJ IDEA ne dopušta. Budući da deklaracije jvmMaina ne ulaze u KLIB metapodatke, IDE za takve pozive prikazuje pogrešku „unresolved reference", dok ih prevoditelj šutke prihvaća i kod se izgradi bez pogrešaka.

Uključivanje odvojene kompilacije tjera kompajler da se ponaša strože, usklađujući njegovo ponašanje s IDE-om. Opcionalna zastavica dodaje se u gradle.properties kao kotlin.kmp.separateCompilation=true, a funkcija je prema zadanim postavkama isključena jer je eksperimentalna. S uključenom opcijom kôd koji se prije gradi sada pada: na primjer, poziv Foo iz app/commonMain, kada je class Foo deklariran samo u lib/jvmMain, postaje pogreška kompilacije, a ne upozorenje samo u IDE-u. Rješenje je uspostaviti eksplicitnu vezu pomoću expect/actual: deklarirati expect class Foo u lib/commonMain i označiti platformsku verziju kao actual class Foo. Isto pravilo vrijedi za zajedničke testne skupove commonTest, jer testovi „ovise” o glavnom kodu; commonTest preveden protiv jvmMain više ne može dosegnuti platformsku deklaraciju bez odgovarajućeg expect u commonMain.

Shema također uklanja odstupanja pri odabiru preopterećenja. Kad u lib/commonMain i lib/jvmMain postoji nekoliko preopterećenja, IDE i prevodilac trenutno odabiru različite deklaracije. Na primjer, pri fun foo(x: Any) u commonMain i fun foo(x: String) u jvmMain IDE rješava foo("") zajedničkom preopterećenju Any, ali kompilacija odabire konkretnije preopterećenje String, a u vremenu izvođenja ispisuje se "platform". Kod odvojene kompilacije poziv se razrješava na zajedničku deklaraciju, i ispisuje se "common", što se podudara s ponašanjem IDE-a. Zamršeniji kvar pojavljuje se kada se pogreška pojavljuje samo u platformskom kodu: ako actual-klase dodaju supertipove kojih nema kod njihovih expect-pandanâ, zaključivanje povratnog tipa može se na JVM-u svesti na Any i pokvariti pristup .nameuzrokujući greške pri izgradnji samo na JVM-u.

Kod odvojene kompilacije app/commonMain razrješava pozive samo prema deklaracijama iz eventsLib/commonMain, pa kompilator izvodi ispravan povratni tip Eventi kompilacija za JVM završava uredno. JetBrains dokumentira poznate probleme koji se još ispravljaju. Najvažnije: da bi odvojena kompilacija ispravno radila, autori biblioteka moraju objavljivati metapodatke KLIB — to je već zadano ponašanje zadataka za objavljivanje KMP Gradle dodatka, ali nova shema čini te KLIB-ove obveznima. Budući da je značajka Experimental, očekujte strože provjere u fazi kompilacije i planirajte prema tome prilagoditi postojeći kod.