Kotlin support
Drop a .kt file into your sources and the rest happens automatically. No [kotlin] section to fill in, no plugin to add, no kotlinc to install.
Detection
On every build Curie scans the source roots. If at least one .kt file is present, it switches into mixed mode: the Kotlin compiler and the Kotlin stdlib are resolved from Maven Central (cached in ~/.m2 after the first run), and compilation runs in two phases.
Two-phase compile
- Phase 1 — kotlinc. Every
.ktand every.javasource is handed to kotlinc together. Kotlin resolves Java types directly from source — no pre-compiled stubs needed — and writes.classfiles totarget/classes/. - Phase 2 — javac. Only the
.javasources are re-compiled, this time withtarget/classes/on the classpath so javac sees the Kotlin bytecode from phase 1. This step is skipped when there are no Java sources.
The two-phase order gives both directions of interop. Java can call Kotlin types (resolved against phase-1 bytecode); Kotlin can call Java types (resolved against source in phase 1).
Source layouts
Three layouts coexist in the same project:
- Maven Java —
src/main/java/com/foo/Bar.java - Maven Kotlin —
src/main/kotlin/com/foo/Bar.kt - Flat-package —
src/com.foo/Bar.kt(the directory name is the package;.javaand.ktcan share a single dot-named directory)
Main class auto-detection
For Kotlin fun main() declarations, Curie infers the synthetic FileNameKt class name and writes it into the JAR's Main-Class manifest entry. You can still set mainClass in Curie.toml to override.
package com.example fun main() { println("Hello from Curie (Kotlin)!") }
Build the project and Curie picks com.example.HelloKt automatically — no mainClass required in Curie.toml.
Stale-class detection
Removing a top-level type from a .kt file — or deleting a .kt file outright — leaves orphan .class files in target/classes that the next javac run could silently re-pick-up. Curie cleans them in three steps:
- Before kotlinc runs, every
.classfile whose JVMSourceFileattribute ends in.ktis deleted. - kotlinc compiles the current source set and re-emits exactly the classes still produced. Anything not re-emitted stays gone.
- A canonical Kotlin source list is stamped to
target/.kt-sourcesso a pure deletion (which leaves no mtime to compare against) still triggers a recompile next time.
The Java equivalent — driven by a source→class manifest the javac wrapper emits — covers the same cases for .java files. See Incremental builds › stale detection.
Worked example
A Java entry point calling into a Kotlin data class — both files in the same flat-package directory:
package com.example.mixed data class Greeting(val name: String) { fun message(): String = "Hello, $name, from Kotlin!" }
package com.example.mixed; public class Main { public static void main(String[] args) { Greeting g = new Greeting("Curie"); System.out.println(g.message()); } }
Building hello-mixed v0.1.0 Resolve Kotlin 8 JAR(s) Compile 2 source file(s) [no class files] Tests ✔ 2 tests successful Done target/hello-mixed-0.1.0.jar