Architecture
How kwenza works, in brief.
Resolving dependencies
kwenza reads each dependency’s POM from Maven Central, following <parent> POMs and BOM imports
the way Maven does. It fetches one level of the dependency tree at a time, in parallel.
When two versions of the same library meet, the one nearest your project wins, and a version you declare yourself always wins. That’s Maven’s rule, so kwenza resolves the same versions Maven and Gradle would.
Every jar is checked against the repository’s published checksum before it’s used. Everything is
cached in ~/.m2/repository, in Maven’s layout, so kwenza shares one cache with Maven and Gradle.
KWENZA_REPO points it somewhere else.
The lockfile
kwenza.lock records every resolved version and its sha256. While your declared dependencies
are unchanged, builds use the lock and skip the network, and still check each jar against it.
Changing kwenza.toml resolves again and rewrites the lock.
Compiling
kwenza compiles in-process with the JDK’s own compiler. java-version becomes javac --release,
which needs a JDK at least that new, so kwenza checks that first and says which JDK you need.
Annotation processors don’t run: kwenza passes -proc:none, so a processor shipped inside a
dependency is never picked up from the classpath, whichever JDK you use.
Packaging
kwenza package writes a thin jar: your classes and resources, plus a Class-Path entry listing
the dependency jars in your local cache. It runs with java -jar on the machine that built it.
Running and testing
kwenza run and kwenza test start a fresh JVM for your code, so a call to System.exit() can’t
take kwenza down with it. Tests run on the JUnit Platform, which finds any test engine on your
test classpath.
Startup time
kwenza starts a fresh JVM every time instead of keeping a background daemon. Its own work takes
about 400 ms on a small project; starting the JVM and loading the compiler adds roughly as much
again. That’s why a warm Gradle daemon can be quicker on very small projects. KWENZA_TIMING
shows the split.