-
Notifications
You must be signed in to change notification settings - Fork 148
Android Gradle Plugin 9 Restructure
This document summarizes the structural changes made to the Korge project to adopt Android Gradle Plugin (AGP) 9.x, why each change was needed, and how the result is verified.
AGP 9 changes two fundamental rules that the old Korge project layout violated:
-
An Android module is now strictly either an application or a library — never both.
com.android.application(which produces an APK) can no longer coexist with Kotlin-Multiplatform library configuration in the same module. A game therefore can no longer be a single module that simultaneously holds the shared game code and acts as the Android application. -
The legacy
com.android.libraryplugin is gone. KMP library modules must use the newcom.android.kotlin.multiplatform.libraryplugin instead.
In addition, AGP 9 ships built-in Kotlin support: application modules must not apply
org.jetbrains.kotlin.android anymore (doing so now fails the build).
To satisfy these rules, the engine's "core" must be pure shared library code, and each
platform's entry point (the thing that actually launches the game: an Android
Activity, a JVM main, a browser bootstrap, an iOS framework) must live in its own
application module that depends on the shared library. This is the "shared library + one
application module per entry point" pattern that this release establishes.
A secondary goal was to remove the kproject Gradle plugin from the active build. It
was a source-/git-based dependency mechanism that is no longer wanted and is unrelated to
the AGP 9 migration; it has been archived for possible later revival rather than deleted.
Much of the engine was already AGP-9-ready before this work:
- AGP was already
9.2.0ingradle/libs.versions.toml. - The legacy
com.android.libraryplugin had already been fully removed; onlycom.android.kotlin.multiplatform.librarywas in use. -
korgeandkorge-corewere already clean KMP libraries.
So the remaining work was concentrated in the example/app layer (korge-sandbox,
e2e/*) and in the Gradle plugin build (kproject removal, Java-version packaging),
not in the engine libraries themselves.
kproject is deeply self-contained: nothing in the main korge-gradle-plugin imported it,
and the entire korge-gradle-plugin-settings module existed only to host it.
- Moved (with history preserved) into a new top-level
_kproject-archive/directory, which is not part of any Gradle build:-
korge-gradle-plugin-common/.../org/korge/kproject/**andSettingsKProjectExt.kt(plus the matching tests and test resource). -
korge-gradle-plugin/.../org/korge/kproject/**(KProjectPlugin,KProjectRootPlugin,AndroidConfig). - The whole
korge-gradle-plugin-settings/module.
-
- Removed the
kproject/kprojectRootplugin registrations fromkorge-gradle-plugin/build.gradle.kts. - Removed
:korge-gradle-plugin-settingsfromkorge-gradle-plugins/settings.gradle.kts. - Removed the now-unused
jgitdependency fromkorge-gradle-plugin-common(it existed only for kproject's git resolution).
Result: the gradle-plugins composite build no longer references kproject and still
publishes korge-gradle-plugin and korge-gradle-plugin-common.
Previously korge-sandbox was shared (which mixed game code, the JVM launcher and the
Korge "executable" plugin) plus a stub androidApp. It is now a clean reference for the
new layout:
-
shared— a pure KMP library (kotlin.multiplatform+com.android.kotlin.multiplatform.library) holding all the game/sample code, withjvm,android,js,wasmJsandios*targets, depending onprojects.korge. It no longer applies the Korge executable plugin and contains no launcher. -
jvmApp— desktop entry point (application+kotlin.jvm), runsJvmMain(moved here fromshared), depends on:shared. -
androidApp— Android entry point:com.android.applicationonly (nokotlin-android, per AGP 9), withMainActivity : KorgwActivityand anAndroidManifest.xml, depending on:shared.minSdkis raised to ≥21 and multidex is enabled because the engine exceeds the 64K method limit. -
webApp— JS + WASM browser entry point (kotlin.multiplatform,binaries.executable()), delegating to the shared entry, depending on:shared. -
iosApp— iOS framework that exports:shared. It is gated to macOS insettings.gradle.ktsbecause linking an Apple framework only works on macOS; this keeps the build green on Windows/Linux.
To let every app module call the game uniformly, the shared application entry point (Main.kt,
helpers.kt, MainRenderImagesJvmNative.kt, and JvmMain) was moved out of the default
(root) Kotlin package into package org.korge.application. Root-package declarations cannot
be imported from a packaged file, which made multi-module entry points impossible before.
Root settings.gradle.kts now includes
:korge-sandbox:{shared, jvmApp, androidApp, webApp} (and :iosApp only on macOS).
The project targets Java 21 (javaTargetCompatibility = 21), and consumers such as the
e2e builds run on Java 21. When the plugins were published from a Java 22 JVM, the
resulting korge-gradle-plugin artifact was tagged "requires JVM 22" and compiled to Java
22 bytecode, so Java-21 consumers failed with UnsupportedClassVersionError / "requires at
least JVM runtime version 22".
Fixes so the published plugin is always Java-21-compatible regardless of the building JDK:
- Added a Java source/target compatibility block to
korge-gradle-plugin/build.gradle.kts(matchingkorge-gradle-plugin-common), pinning the published metadata. - Added
korge-gradle-plugins/gradle/gradle-daemon-jvm.properties(copied from the root build) so the plugins build runs on a Java 21 daemon → Java-21 bytecode. - Added a proper Gradle wrapper to
korge-gradle-plugins/(it previously had none), so the plugins can be published directly "from their own folder" withcd korge-gradle-plugins && ./gradlew publishToMavenLocal.
-
e2e/e2e-test— already follows the split (a Korgesharedmodule that also drives the JVM screenshot test harness, plus a separateandroidApp). It now assembles cleanly against the freshly published artifacts; no structural change was required. -
e2e/e2e-test-multi— had a pre-existing breakage unrelated to AGP 9: itssettings.gradlesourced a deleted file (gradle/repositories.settings.gradle) viaEval.xy, so it could not even configure. The repositories are now declared inline insettings.gradle, and the project assembles.
All commands below were run and succeeded:
# 1. Engine libraries + the restructured sandbox (root build)
./gradlew publishToMavenLocal
# -> publishes korge, korge-core, korge-ipc, korge-reload-agent (+ all platform variants)
# 2. Gradle plugins, published from their own folder
cd korge-gradle-plugins && ./gradlew publishToMavenLocal
# -> publishes korge-gradle-plugin, korge-gradle-plugin-common as Java-21 artifacts
# 3. Sandbox entry-point modules
./gradlew :korge-sandbox:jvmApp:compileKotlin \
:korge-sandbox:webApp:compileKotlinJs :korge-sandbox:webApp:compileKotlinWasmJs \
:korge-sandbox:androidApp:assembleDebug # produces androidApp-debug.apk
# 4. e2e projects (consume the published artifacts)
cd e2e/e2e-test && ./gradlew assemble # jvm + js + android
cd e2e/e2e-test-multi && ./gradlew assemble # via root wrapper: ./gradlew -p e2e/e2e-test-multi assemble-
e2e/e2e-test-integration(SwiftUI sample) depends on theorg.korge.engine.settings(kproject) plugin, which has been archived. It is intentionally left untouched and will not build until kproject is revisited. This matches the decision to defer kproject. -
iOS modules (
korge-sandbox:iosApp, iOS framework linking) can only be built on macOS; they are gated out of the Windows/Linux build and were not exercised here. - The
_kproject-archive/directory is retained outside the build purely so kproject can be resurrected later; it can be deleted entirely if that is no longer desired.