Skip to content

Multiplatform projects

A Kargo project can target several platforms at once: jvm, wasm, js, native and android. Code in src/ is compiled for all of them; code in src-<target>/ only for one.

Terminal window
kargo new hello -t jvm,wasm,native
kargo.toml
[package]
name = "hello"
version = "0.1.0"
group = "com.example"
[target.jvm]
[target.wasm]
environment = "js"
[target.native]
platforms = ["host"]
kind = "executable"

To add a target to an existing project, use kargo target add:

Terminal window
kargo target add wasm native

It appends the same [target.<name>] sections kargo new writes, without touching the rest of kargo.toml, and creates an empty src-<target>/ for platform-specific code. kargo target remove wasm disables a target again (its sources stay on disk), and kargo target list shows what is enabled. See kargo target.

Declare what you need in common code with expect, then provide an actual for every target:

src/Platform.kt
package com.example.hello
expect fun platformName(): String
fun greeting(): String = "Hello from ${platformName()}!"
fun main() {
println(greeting())
}
src-jvm/Platform.kt
package com.example.hello
actual fun platformName(): String = "the JVM (Java ${System.getProperty("java.version")})"
src-wasm/Platform.kt
package com.example.hello
actual fun platformName(): String = "WebAssembly"
src-native/Platform.kt
package com.example.hello
import kotlin.experimental.ExperimentalNativeApi
@OptIn(ExperimentalNativeApi::class)
actual fun platformName(): String = "Kotlin/Native on ${Platform.osFamily}"
Terminal window
$ kargo build # all targets
$ kargo run # the first of: jvm, native, wasm, js, android
Hello from the JVM (Java 21.0.5)!
$ kargo run -t native
Hello from Kotlin/Native on LINUX!
$ kargo run -t wasm
Hello from WebAssembly!
$ kargo test # tests on every target
$ kargo test -t native
$ kargo test --no-fail-fast # keep testing the other targets when one fails

kargo test stops at the first target that fails; --no-fail-fast (or [test] fail-fast = false) tests them all and prints a result per target. kargo build --keep-going builds every target and reports each failure. See Testing every target.

[target.js] compiles the same common code to JavaScript with Kotlin/JS, for consumers that can’t use Wasm. Its own code goes in src-js/ and tests-js/, and its own dependencies in [target.js.dependencies]:

kargo.toml
[target.jvm]
[target.js]
environment = "node"
src-js/Platform.kt
package com.example.hello
actual fun platformName(): String = "Kotlin/JS"
Terminal window
$ kargo run -t js
Hello from Kotlin/JS!

Wasm and JS are separate targets in Kargo, as in Gradle (wasmJs() and js()), so a project that enables both writes an actual for each. See JavaScript.

Multiplatform libraries go in [dependencies]; Kargo picks the right artifact for each target using Gradle module metadata. Platform-specific libraries go in [target.<name>.dependencies]:

kargo.toml
[dependencies]
"org.jetbrains.kotlinx:kotlinx-datetime" = "0.7.1"
"com.squareup.okio:okio" = "3.10.2"
[target.jvm.dependencies]
"org.slf4j:slf4j-simple" = "2.0.17"

Using a JVM-only library in src/ fails to compile on the other targets. That’s your cue to move the code to src-jvm/.

  • Keep business logic in src/ and put only the thin platform layer in src-<target>/.
  • Prefer multiplatform libraries (klibs.io lists them) over java.* APIs in common code. For example, use kotlinx-datetime instead of java.time, and okio or kotlinx-io for files.
  • Tests in tests/ run on every target, which is a cheap way to catch platform differences.
  • kargo doc documents src/ as common code and shows which platforms each declaration is available on. See API documentation.
  • In an editor, kargo lsp treats src/ and each src-<target>/ as separate source sets, so expect and actual declarations link up and common code only sees common code and the libraries every target has. Kargo’s own Kotlin server reads the code of every target; with JetBrains’ Kotlin LSP, common code is analyzed as JVM code and Wasm, JS and Native code gets no Kotlin code intelligence yet. See Multiplatform projects.
  • kargo publish publishes a multiplatform library the way the Kotlin Gradle Plugin does, with one module per platform, so Gradle and Kargo projects pick the right artifact. See Publishing a library.