Das neue Schema der getrennten Kompilierung von Kotlin bringt Multiplattform-Builds mit der IDE in Einklang

  • PublicPublic
  • 28 Sep, 2026
Das neue Schema der getrennten Kompilierung von Kotlin bringt Multiplattform-Builds mit der IDE in Einklang

Der bestehende Kompilierungsansatz von Kotlin Multiplatform funktioniert, kann aber unerwartetes und schwer vorhersehbares Verhalten erzeugen. Der Kern des Problems liegt in der Diskrepanz zwischen IDE und Compiler: Sie lösen Aufrufe aus gemeinsamen Source-Sets auf Deklarationen aus Abhängigkeiten unterschiedlich auf. In Kotlin 2.5.0-Beta1 hat JetBrains eine optionale „getrennte Kompilierung“ hinzugefügt, die dies beheben soll — sie bringt das Kompilierungsergebnis mit der Analyse in der IDE in Einklang und verweist präziser auf problematische Aufrufe aus gemeinsamen Source-Sets in Bibliothekscode. Als Bonus aktiviert das Schema außerdem die inkrementelle Kompilierung für gemeinsame Source-Sets.

Das Problem hat seine Wurzeln im Aufbau eines KMP-Moduls. Ein Modul mit gemeinsamem Code enthält normalerweise mehrere Source-Sets: ein gemeinsames (commonMain) und plattformspezifische Sets (jvmMain für Kotlin/JVM). Während der Kompilierung eines plattformspezifischen Source-Sets wird der Code in jvmMain plus alle verbundenen gemeinsamen Sets gegen die Plattformartefakte der Abhängigkeiten kompiliert. Dadurch erhält Code in commonMain die Möglichkeit, sich implizit auf eine Deklaration aus jvmMain in einer Abhängigkeit aufzulösen — genau das erlaubt IntelliJ IDEA jedoch nicht. Da jvmMain-Deklarationen nicht in die KLIB-Metadaten gelangen, zeigt die IDE für solche Aufrufe einen „unresolved reference“-Fehler an, während der Compiler sie stillschweigend akzeptiert und der Code ohne Fehler gebaut wird.

Mit aktivierter separater Kompilierung verhält sich der Compiler strenger und gleicht sein Verhalten an die IDE an. Das optionale Flag wird in gradle.properties als kotlin.kmp.separateCompilation=truehinzugefügt, und standardmäßig ist die Funktion deaktiviert, weil sie Experimental ist. Mit aktivierter Option schlägt Code, der zuvor kompiliert wurde, nun fehl: Beispielsweise wird der Aufruf Foo aus app/commonMain, wenn class Foo nur in lib/jvmMain deklariert ist, wird zu einem Kompilierungsfehler statt zu einer reinen IDE-Warnung. Die Lösung besteht darin, eine explizite Verbindung über expect/actual herzustellen: deklariere expect class Foo in lib/commonMain und die Plattformversion als actual class Foozu markieren. Dieselbe Regel gilt für die gemeinsamen Testsourcengruppen commonTest, schließlich „hängen“ Tests vom Haupt-Code ab; commonTest, das gegen jvmMain kompiliert wird, kann die Plattformdeklarationen ohne ein passendes expect in commonMain nicht mehr erreichen.

Das Schema behebt auch Abweichungen bei der Overload-Auswahl. Wenn in lib/commonMain und lib/jvmMain mehrere Overloads vorhanden sind, wählen IDE und Compiler derzeit unterschiedliche Deklarationen aus. Zum Beispiel bei fun foo(x: Any) in commonMain und fun foo(x: String) in jvmMain löst die IDE auf foo("") zur allgemeinen Überladung Any, aber die Kompilierung wählt die spezifischere Überladung Stringund zur Laufzeit wird „platform“ ausgegeben. Bei separater Kompilierung wird der Aufruf zur gemeinsamen Deklaration aufgelöst und „common“ ausgegeben, was dem Verhalten der IDE entspricht. Ein kniffligerer Fehler tritt auf, wenn die Fehlermeldung nur im plattformspezifischen Code erscheint: Wenn actual-Klassen Supertypen hinzufügen, die ihre expect-Gegenstücke nicht haben, kann die Ableitung des Rückgabetyps auf der JVM auf Any zusammenschrumpfen .name, was Build-Fehler nur auf der JVM verursacht.

Bei getrennter Kompilierung löst app/commonMain Aufrufe nur anhand von Deklarationen aus eventsLib/commonMain auf, sodass der Compiler den korrekten Rückgabetyp ableitet Event, und die Kompilierung für die JVM läuft sauber durch. JetBrains dokumentiert bekannte Probleme, die noch behoben werden. Das Wichtigste: Damit die getrennte Kompilierung korrekt funktioniert, müssen Bibliotheksautoren KLIB-Metadaten veröffentlichen — das ist bereits das Standardverhalten der Veröffentlichungs-Tasks des KMP-Gradle-Plugins, aber das neue Schema macht diese KLIBs unverzichtbar. Da die Funktion Experimental ist, sind strengere Prüfungen zur Kompilierzeit zu erwarten; plane entsprechend, bestehenden Code anzupassen.