Gradle Plugin Integration
Kanvas deliberately uses the root Gradle project as its control plane.
The plugin rejects being applied to a child project and then uses kanvas.target to identify the project that actually owns the desktop application tasks.
A renderer integration is wider than a single source set. Kanvas may need to resolve an application task in one module, build native artifacts outside that module, inject a Java agent into a JavaExec task, and modify packaging output. Root ownership gives those operations one stable coordination point.
See KanvasPlugin.apply.
Task indirection
The plugin exposes stable Kanvas lifecycle tasks:
kanvasDoctor
kanvasCompatibility
kanvasRuntime
kanvasRefreshRuntime
kanvasBuild
kanvasRun
kanvasPackage
Those tasks do not require every Kotlin repository to use the same module name or application task name. After project evaluation, Kanvas resolves the configured target and chooses the real build/run/package tasks.
That is why configuration has both:
kanvas {
target = ":desktop"
// Only needed when automatic detection is wrong.
buildTask = ""
runTask = ""
packageTask = ""
}
Automatic detection is convenience. Explicit task configuration is the escape hatch that keeps the plugin usable in unusual multi-module repositories.
Launch-time injection
For supported JavaExec run tasks, Kanvas injects:
- the Java instrumentation agent
- backend selection
- takeover/capture/strict flags
- audit and capture output paths
- the native library override when one is supplied
The plugin currently emits both the kanvas.* property names and older kotlin.display.* names for runtime compatibility.
Source: KanvasPlugin.kt
Why this is not a custom application framework
Kanvas needs to alter how a desktop app is launched and rendered, not how users structure domain code, state, or composables. Keeping the integration at Gradle + runtime instrumentation means application source can remain conventional Kotlin/Compose code.