Migrating from Gradle
This guide moves a Gradle project, JVM or Kotlin Multiplatform, to Kargo. Most single-module projects take about fifteen minutes.
Step 1: Create kargo.toml
Section titled “Step 1: Create kargo.toml”Start from your build.gradle.kts:
plugins { kotlin("jvm") version "2.4.21" kotlin("plugin.serialization") version "2.4.21" application}
group = "com.example"version = "1.2.0"
repositories { mavenCentral() maven("https://jitpack.io")}
dependencies { implementation(platform("io.ktor:ktor-bom:3.6.0")) implementation("io.ktor:ktor-client-cio") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0") implementation("com.github.ajalt.clikt:clikt:5.0.3") runtimeOnly("ch.qos.logback:logback-classic:1.5.32") compileOnly("org.jetbrains:annotations:26.0.2") testImplementation(kotlin("test")) testImplementation("io.mockk:mockk:1.14.5")}
kotlin { jvmToolchain(21) compilerOptions { freeCompilerArgs.add("-Xcontext-parameters") optIn.add("kotlin.ExperimentalStdlibApi") }}
application { mainClass.set("com.example.cli.MainKt") applicationDefaultJvmArgs = listOf("-Xmx512m")}The Kargo equivalent:
[package]name = "cli"version = "1.2.0"group = "com.example"
[kotlin]version = "2.4.21"compiler-args = ["-Xcontext-parameters"]opt-in = ["kotlin.ExperimentalStdlibApi"]
[toolchain]jdk = "21"
[target.jvm]jvm-target = "21"main-class = "com.example.cli.MainKt" # optional: Kargo finds `fun main` by itselfjvm-args = ["-Xmx512m"]
[frameworks]serialization = true # plugin + kotlinx-serialization-json
[dependencies]"io.ktor:ktor-bom" = { version = "3.6.0", bom = true }"io.ktor:ktor-client-cio" = {}"com.github.ajalt.clikt:clikt" = "5.0.3""ch.qos.logback:logback-classic" = { version = "1.5.32", scope = "runtime" }"org.jetbrains:annotations" = { version = "26.0.2", scope = "provided" }
[dev-dependencies]"io.mockk:mockk" = "1.14.5" # kotlin("test") is always included
[repositories]jitpack = "https://jitpack.io" # mavenCentral() is always includedStep 2: Move the sources
Section titled “Step 2: Move the sources”Kargo uses flat directories:
| Gradle | Kargo |
|---|---|
src/main/kotlin, src/main/java |
src/ |
src/main/resources |
resources/ |
src/test/kotlin, src/test/java |
tests/ |
src/test/resources |
test-resources/ |
src/benchmark/kotlin, src/jmh/kotlin |
benches/ |
git mv src/main/kotlin src-tmp && git mv src/test/kotlin testsgit mv src/main/resources resources 2>/dev/null; git mv src/test/resources test-resources 2>/dev/nullrm -rf src && git mv src-tmp src# Java sources, if any: git mv src/main/java/* src/For Kotlin Multiplatform:
| Gradle source set | Kargo |
|---|---|
src/commonMain/kotlin |
src/ |
src/jvmMain/kotlin |
src-jvm/ |
src/wasmJsMain/kotlin |
src-wasm/ |
src/jsMain/kotlin |
src-js/ |
src/nativeMain/kotlin, src/linuxX64Main/kotlin, … |
src-native/ |
src/androidMain/kotlin |
src-android/ |
src/commonTest/kotlin |
tests/ |
src/jvmTest/kotlin |
tests-jvm/ |
Step 3: Delete Gradle
Section titled “Step 3: Delete Gradle”git rm -r build.gradle.kts settings.gradle.kts gradle.properties gradlew gradlew.bat gradle/echo "/target" >> .gitignoreKeep gradle/libs.versions.toml open while you finish step 1 if you used a version catalog; Kargo doesn’t read catalogs, so copy the versions into kargo.toml.
Step 4: Build and compare
Section titled “Step 4: Build and compare”kargo buildkargo testkargo tree # compare with ./gradlew dependencies --configuration runtimeClasspathKargo resolves conflicts like Gradle (highest version wins), so the trees should match. If something differs, pin it with force = true.
Reference: Gradle → Kargo
Section titled “Reference: Gradle → Kargo”| Gradle | Kargo |
|---|---|
kotlin("jvm") version "X" |
[kotlin] version = "X" and [target.jvm] |
kotlin("multiplatform") with jvm(), wasmJs(), linuxX64() |
[target.jvm], [target.wasm], [target.native] platforms = ["linux_x64"] |
compilations["main"].cinterops { create("sqlite") { ... } } |
[target.native.cinterop.sqlite], with the .def file in interop/sqlite.def (or headers and the other .def keys in kargo.toml) |
linkerOpts.linux = ... in a .def file, or per-target cinterops blocks |
[target.native.cinterop.sqlite.platform.linux_x64] |
binaries.framework { baseName = "Shared"; export(...) } |
[target.native] kind = "framework", export = [...] |
XCFramework() + assembleXCFramework |
kargo build --xcframework |
A CMake or exec task that builds bundled C code |
[target.native.c-sources] |
wasmWasi() |
[target.wasm] environment = "wasi" |
js { nodejs() }, js { browser() } (js(IR)) |
[target.js] environment = "node" or "browser"; see JavaScript |
useEsModules(), useCommonJs(), the default UMD |
[target.js] module-kind = "es" (Kargo’s default), "commonjs", "umd" |
binaries.library() with generateTypeScriptDefinitions() |
[target.js] kind = "library" (declarations included) |
jsMain.dependencies { implementation(npm("x", "1.0")) } |
[target.js.npm-dependencies] x = "1.0" |
com.android.application + android { ... } |
[target.android] |
signingConfigs { create("release") { ... } } |
[target.android.signing.release], with passwords in store-password-env / key-password-env |
buildTypes { release { isMinifyEnabled = true; proguardFiles(...) } } |
[target.android] minify = true, proguard-files = [...] |
buildConfigField("String", "API_URL", "\"...\"") |
[target.android] build-config = { API_URL = "..." } |
flavorDimensions + productFlavors { create("free") { ... } } |
flavor-dimensions and [target.android.flavors.free] |
./gradlew bundleRelease |
kargo package -t android --release |
implementation(...) |
[dependencies] |
api(...) |
exported = true in [dependencies] |
include(":core") in settings.gradle.kts |
[workspace] members = ["core"] (or a glob such as "libs/*") |
implementation(project(":core")) |
core = { path = "../core" } |
api(project(":core")) |
core = { path = "../core", exported = true } |
java-test-fixtures + testImplementation(testFixtures(project(":core"))) |
test-fixtures/ and core = { path = "../core", test-fixtures = true } in [dev-dependencies] |
subprojects { ... } / convention plugins for shared settings |
[workspace.package], [workspace.kotlin], [workspace.dependencies], [workspace.frameworks] |
./gradlew :server:run |
kargo run -p server |
runtimeOnly(...) |
scope = "runtime" |
compileOnly(...) |
scope = "provided" |
annotationProcessor(...) |
scope = "processor" |
options.compilerArgs (JavaCompile) |
[target.jvm] javac-args |
ksp(...) (the com.google.devtools.ksp plugin) |
scope = "ksp" |
kspTest(...) |
scope = "ksp" in [dev-dependencies] |
kapt(...) |
not supported; use the library’s KSP processor (scope = "ksp") |
testImplementation(...) |
[dev-dependencies] |
jvmMainImplementation(...) / sourceSets.jvmMain.dependencies |
[target.jvm.dependencies] |
platform(...) / enforcedPlatform(...) |
bom = true |
exclude(group = "g", module = "m") |
exclude = ["g:m"] |
isTransitive = false |
transitive = false |
strictly(...) / force |
force = true |
kotlin("plugin.serialization") |
[frameworks] serialization = true |
org.jetbrains.kotlinx.benchmark with benchmark { targets { register("main") } }, me.champeau.jmh, then ./gradlew benchmark or jmh |
[frameworks] benchmark = true and kargo bench; the configuration’s iterations, warmups, iterationTime, mode and outputTimeUnit are [bench] iterations, warmup, mode and time-unit (see Benchmarking) |
kotlin("plugin.spring"), org.springframework.boot |
[frameworks] spring-boot = true |
kotlin("plugin.jpa") |
spring-boot = { starters = ["data-jpa"] } or jpa = true |
org.jetbrains.compose + kotlin("plugin.compose") |
[frameworks] compose = true |
io.ktor.plugin |
[frameworks] ktor = { server = "netty" } |
io.freefair.lombok + kotlin("plugin.lombok") |
[frameworks] lombok = true |
| Other compiler plugins | [plugins.<name>] maven = "...", id = "..." |
jvmToolchain(21) |
[toolchain] jdk = "21" |
org.beryx.jlink + ./gradlew jlink |
kargo package --format jlink, with [package.jlink] modules for extra modules |
org.graalvm.buildtools.native + ./gradlew nativeCompile |
kargo package --format native-image, with [toolchain] graalvm and [package.native-image] |
-Pagent / nativeCompile with the tracing agent |
kargo run --native-agent |
org.beryx.jlink’s jpackage task |
kargo package --format installer, with [package.installer] |
Compose’s compose.desktop { application { nativeDistributions { ... } } } and packageDistributionForCurrentOS |
kargo package --desktop, with [package.installer]: targetFormats is type, packageName is name, windows.iconFile and friends are icon, fileAssociation(...) is file-associations, macOS.signing is signing-identity and macOS.notarization is notarize |
Compose’s packageUberJarForCurrentOS |
kargo package --desktop --format jar (--platforms all for every OS) |
compose.resources { packageOfResClass = ...; publicResClass = true } and src/commonMain/composeResources/ |
[frameworks] compose = { resources-package = "...", public-resources = true } and composeResources/ |
jvmTarget = JvmTarget.JVM_17 |
[target.jvm] jvm-target = "17" |
freeCompilerArgs |
[kotlin] compiler-args |
optIn |
[kotlin] opt-in |
allWarningsAsErrors = true |
[kotlin] warnings-as-errors = true |
application { mainClass } |
[target.jvm] main-class |
shadowJar |
kargo package (--fat for a library) |
maven-publish + signing (or com.vanniktech.maven.publish) |
kargo publish, with [publish] and the POM fields in [package]; see Publishing a library |
./gradlew publishToMavenLocal |
kargo publish --local |
mavenLocal() |
[repositories] local = "maven-local" |
tasks.register("x") { exec ... } |
[tasks] x = "..." |
tasks.compileKotlin { dependsOn(gen) } |
gen = { run = "...", hooks = ["pre-build"] } |
com.google.protobuf plugin, sourceSets { main { kotlin.srcDir("build/generated/...") } } |
a [[generate]] step such as builtin = "protobuf"; its output directory is a source root |
./gradlew run --args="a b" |
kargo run -- a b |
extra JavaExec tasks for other mains |
[[bin]] and kargo run --bin <name>, or kargo run --main <class> |
./gradlew test --tests "*Foo*" |
kargo test Foo |
./gradlew dependencies |
kargo tree |
./gradlew dependencyUpdates (Versions plugin) |
kargo outdated, then kargo upgrade |
./gradlew dependencies --update-locks g:a |
kargo update g:a |
./gradlew dokkaGenerate (Dokka plugin) |
kargo doc |
./gradlew ktlintFormat / ktlintCheck (ktlint plugins, Spotless) |
kargo fmt / kargo fmt --check |
./gradlew detekt (detekt plugin) |
kargo lint; keep your config with [lint] config = ["config/detekt/detekt.yml"] |
--offline |
--offline |
kotlin.incremental=true (incremental compilation) |
On by default for the JVM; KARGO_INCREMENTAL=false turns it off |
Gradle daemon / Kotlin daemon (--no-daemon, kotlin.compiler.execution.strategy) |
The compiler daemon (KARGO_DAEMON=false, kargo daemon stop) |
org.gradle.parallel=true |
Always on: targets, Native platforms and independent workspace members build in parallel (-j limits members) |
gradle.lockfile |
kargo.lock (always on) |