Gradle Plugin

The Gradle plugin contributes three tasks to any Gradle build: kumlRender, kumlGenerate, and kumlValidate. All three are @CacheableTask with proper @InputDirectory / @OutputDirectory annotations — incremental builds skip unchanged scripts.

The dev.kuml Gradle plugin is published on the Gradle Plugin Portal and on Maven Central (group dev.kuml, artefact kuml-gradle-plugin) since v0.32.0.

Apply the plugin

// build.gradle.kts
plugins {
    id("dev.kuml") version "{kuml-version}"
}

That’s all you need to get the three tasks registered. Defaults are sensible:

  • Source directory: src/main/kuml/

  • Output directory: ${buildDir}/kuml/

  • Format: svg

  • Theme: kuml

  • Generator: kotlin

Override any of these in the kuml { …​ } extension block.

Configuration block

kuml {
    sourceDir.set(file("src/main/kuml"))
    outputDir.set(layout.buildDirectory.dir("kuml"))
    format.set("svg")                         // "svg" | "png"
    theme.set("elegant")                      // any ThemeRegistry name
    widthPx.set(2048)                         // PNG width (ignored for SVG)
    generator.set("java")                     // "kotlin" | "java" | "sql" | custom
    generatePackage.set("com.example.domain") // Java/Kotlin package
    generateOptions.put("java-style", "records")
    generateOptions.put("sql-dialect", "postgres")
    failOnValidationViolations.set(true)      // fail the build on OCL violations
    transformer.set("uml-to-exposed-via-erm") // kumlTransform: transformer id (no default)
    transformOptions.put("idType", "uuid")    // kumlTransform: transformer context options
    transformModelName.set("merged")          // name of the merged model
    skipNonUmlScripts.set(false)              // kumlTransform: skip C4/SysML scripts instead of failing
    mergeScripts.set(false)                   // kumlGenerate: merge all scripts into one model first
}

Every property is a Gradle lazy Property / DirectoryProperty / MapProperty — you can wire them to project conventions, settings, or other tasks.

kumlRender

./gradlew kumlRender

Walks sourceDir for *.kuml.kts files and renders each to outputDir, mirroring the source’s directory structure:

src/main/kuml/
├── order.kuml.kts
└── billing/
    └── invoice.kuml.kts

# →

build/kuml/
├── order.svg
└── billing/
    └── invoice.svg

Inputs (cacheable):

  • sourceDir (PathSensitive=RELATIVE)

  • format, theme, widthPx

Outputs:

  • outputDir

A second ./gradlew kumlRender with no source changes is UP-TO-DATE.

kumlGenerate

./gradlew kumlGenerate

Runs the configured generator over every diagram in sourceDir. C4 scripts are skipped automatically (only UML diagrams produce code). Output goes to outputDir/generated/ by default.

Wire into other tasks:

tasks.named<JavaCompile>("compileJava") {
    dependsOn("kumlGenerate")
    source(layout.buildDirectory.dir("kuml/generated"))
}

For Kotlin source, point srcDirs of the main source set at the same directory.

By default the generator runs once per script. With mergeScripts.set(true) all UML class scripts are merged into one model first (see kumlTransform for the merge rules) and the generator runs once on it. Scripts that are not UML class diagrams are skipped.

kumlTransform

./gradlew kumlTransform

Merges all *.kuml.kts UML class scripts below sourceDir into one model and applies the configured transformer once. Stub entities that scripts declare only to reference a foreign entity are removed. The merge rules are the same as for kuml transform with several scripts; a conflict fails the build with a message that names the table, both classes, both scripts and the attributes.

kuml {
    sourceDir.set(file("src/main/kuml"))
    transformer.set("uml-to-exposed-via-erm")
    transformOptions.putAll(mapOf("idType" to "uuid", "package" to "com.example.db.generated"))
}

// Wires the output as a source directory and adds the task dependency.
kotlin.sourceSets.named("main") { kotlin.srcDir(tasks.named("kumlTransform")) }
  • Output goes to outputDir/transform/ by default. The directory is cleaned before every run, so outputs of removed scripts disappear. outputDir must not be, lie inside, or contain sourceDir.

  • Scripts are processed in sorted order of their path relative to sourceDir; the result is deterministic.

  • The task is cacheable and compatible with the configuration cache.

  • Limits: at most 1000 scripts and 1 MiB per script by default (maxScripts, maxScriptBytes on the task). Symbolic links that lead out of sourceDir fail the build.

  • A script that is not a UML class diagram (for example C4) fails the build and names the file. Set skipNonUmlScripts.set(true) to skip such scripts instead.

  • The Exposed transformers (uml-to-exposed-via-erm, uml-to-exposed) ship with the plugin. Compiling the generated Exposed code needs the Exposed libraries on your own compile classpath.

kumlValidate

./gradlew kumlValidate

Runs OclValidator over every *.kuml.kts in sourceDir. Hangs on to the build’s check lifecycle:

plugins {
    id("dev.kuml")
    java
}
// check automatically depends on kumlValidate

If failOnValidationViolations = true (default), violations fail the build with a human-readable error listing each violation. If false, the same information is logged as a warning but the build continues — useful for grace-period rollouts.

Multi-project builds

In a multi-project build, apply the plugin only to the projects that own kUML scripts:

// settings.gradle.kts
include(":app", ":domain", ":docs")

// docs/build.gradle.kts
plugins {
    id("dev.kuml")
}
kuml {
    sourceDir.set(file("src/main/kuml"))
    outputDir.set(layout.buildDirectory.dir("kuml"))
}

The plugin is self-contained — no parent project configuration needed.

Reproducibility

The Gradle plugin uses the same scripting host, layout engines, themes, and generators as the CLI. Outputs are byte-identical:

diff <(kuml render src/main/kuml/order.kuml.kts) build/kuml/order.svg
# (no output — files are identical)

That makes CI cache invalidation straightforward: a commit that changes a *.kuml.kts invalidates the corresponding output; everything else stays cached.

Building the plugin from source

The published plugin (see above) is the recommended path for consumers. If you need an unreleased fix or are developing the plugin itself, publish a local copy instead:

./gradlew :kuml-gradle:kuml-gradle-plugin:publishToMavenLocal

Then in your consumer build’s settings.gradle.kts:

pluginManagement {
    repositories {
        mavenLocal()
        gradlePluginPortal()
    }
}

Apply as usual: id("dev.kuml") version "X.Y.Z-SNAPSHOT".