Skip to main content

Same project, four build tools

A single application. One application's worth of dependencies. One main class. Here it is, expressed in bleep.yaml, pom.xml, build.sbt, and build.gradle.kts.

Line counts aren't the point, you'll see sbt's three files come in shorter than bleep's one. The point is the kind of artifact each one is: plain data versus XML versus a DSL with hidden state versus a Kotlin program that mutates a build graph. Read each one with that question in mind.

bleep.yaml, plain data

Same shape across all three languages. The only thing that changes between tabs is the per-language fields (scala:, kotlin:, dep coordinates) and the test framework. Each snippet is a real workspace from bleep's own integration tests; the bleep build server compiles and runs each one on every commit.

$schema: https://raw.githubusercontent.com/oyvindberg/bleep/master/schema.json
$version: 1.0.0-M10
jvm:
name: graalvm-community:25.0.1
projects:
myapp:
extends: template-common
platform:
mainClass: com.example.MainKt
myapp-test:
dependencies:
- io.kotest:kotest-runner-junit5-jvm:5.8.0
- org.junit.jupiter:junit-jupiter:5.10.1
dependsOn: myapp
extends: template-common
isTestProject: true
templates:
template-common:
kotlin:
jvmTarget: '25'
version: 2.3.0
platform:
name: jvm

For the per-feature breakdown of each comparison, see the dedicated pages: vs Maven, vs Gradle, vs sbt, vs Mill.

pom.xml. XML

The Kotlin variant. Java and Scala flavors of pom.xml differ only in the plugin section and dep coordinates, same XML.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>0.1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>2.3.0</kotlin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>io.kotest</groupId>
<artifactId>kotest-runner-junit5-jvm</artifactId>
<version>5.8.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
</plugin>
</plugins>
</build>
</project>

It is 2026. We are still typing closing tags by hand. There is no need.

build.sbt + project/, a DSL across three files

The Scala variant. Sbt is rarely used outside Scala, the Java/Kotlin flavors exist but nobody actually picks sbt for them.

build.sbt:

ThisBuild / scalaVersion := "3.8.3"
ThisBuild / organization := "com.example"

lazy val myapp = (project in file("."))
.enablePlugins(JavaAppPackaging)
.settings(
name := "myapp",
libraryDependencies ++= Seq(
"com.lihaoyi" %% "fansi" % "0.5.0"
),
Compile / mainClass := Some("com.example.Main")
)

project/build.properties:

sbt.version=1.10.0

project/plugins.sbt:

addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.0")

Three files, three default configurations (Compile, Test, Runtime) plus user-defined ones, and a DSL whose every keyword is a global side effect on a hidden settings graph. sbt-native-packager is enabled by name; what it does to your build is documented separately on the plugin's own site.

build.gradle.kts, a Kotlin program

The Kotlin variant. Gradle's Java DSL is similar shape; the Scala flavor exists but isn't widely used.

plugins {
application
kotlin("jvm") version "2.3.0"
}

group = "com.example"
version = "0.1.0-SNAPSHOT"

repositories { mavenCentral() }

dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.8.0")
}

application {
mainClass.set("com.example.MainKt")
}

tasks.withType<JavaCompile> {
sourceCompatibility = "21"
targetCompatibility = "21"
}

A real Kotlin program that side-effects the build graph. Plus settings.gradle.kts, gradle.properties, and the wrapper. Renaming the project means editing rootProject.name in settings.gradle.kts plus a directory rename; there's no gradle rename-project.