Gradle Template

Here are sample configuration files for a multi-project Gradle build. It has the following structure:

project-template/
├── application/
├── build
├── console/
├── gradle/
├── gradle.properties
├── gradlew
├── gradlew.bat
├── readme.md
├── settings.gradle.kts
└── shared/

application, console and shared all represent projects, with their own configuration files.

Info

You can find the source code for this project in the GitLab Public Repository for this course, under sample-code/project-template.

Top-Level (Root)

The project root contains the settings.gradle.kts file, which defines the overall project structure.

// the name of the project
rootProject.name = "multi-project"

// which projects to include
include("application", "console", "shared")

// a way of tracking version numbers globally
// this ensures that we use the same version of libraries in each project
// not every project will use all of these plugins or libraries
// see individual project configuration files for the actual usage
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            // constants
            version("jdk", "17")
            version("javafx", "18.0.2")

            // https://plugins.gradle.org/
            plugin("kotlin-lang", "org.jetbrains.kotlin.jvm").version("1.8.10")
            plugin("jlink", "org.beryx.jlink").version("2.26.0")
            plugin("javafx", "org.openjfx.javafxplugin").version("0.0.13")
            plugin("javamodularity", "org.javamodularity.moduleplugin").version("1.8.12")

            // https://mvnrepository.com/
            library("kotlin-coroutines", "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
            library("sqlite", "org.xerial:sqlite-jdbc:3.40.1.0")
            library("exposed-core", "org.jetbrains.exposed:exposed-core:0.40.1")
            library("exposed-dao", "org.jetbrains.exposed:exposed-dao:0.40.1")
            library("exposed-jdbc", "org.jetbrains.exposed:exposed-jdbc:0.40.1")
            library("junit-jupiter", "org.junit.jupiter:junit-jupiter:5.9.2")
            library("sl4j-api", "org.slf4j:slf4j-api:2.0.6")
            library("sl4j-simple", "org.slf4j:slf4j-simple:2.0.6")
        }
    }
}

It also contains gradle.properties, which includes project definitions.

kotlin.code.style=official

Gradle wrapper settings

There is a top-level gradle directory, containing the Gradle bootstrap files (the means by which Gradle downloads and installs itself when you run gradlew). These files are auto-generated by Gradle when you setup the project.

You might want to update the gradle-wrapper.properties to point to a recent version fo Gradle by changing the distributionURL line. In this example, we’re specifying Gradle 8.0.2.

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Application project

The Application project contains a single src folder, containing the directory tree, and a single build.gradle.kts file which includes the configuration for this specific project. This is a JavaFX application, so we should expect to see application-style plugins and dependencies.

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// notice the syntax for the plugin section uses alias
// this is how we pull in the plugins from the Version Catalog (above)
// e.g. libs.plugins.kotlin.lang inserts the kotlin-lang plugin details.
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    application
    alias(libs.plugins.kotlin.lang)
    alias(libs.plugins.javamodularity)
    alias(libs.plugins.javafx)
    alias(libs.plugins.jlink)
}

// used for packaging only
group = "net.codebot"
version = "1.0-SNAPSHOT"

// telling Gradle to put Java and Kotlin output in the same build structure
// required since we have Java files (module-info.java) and Kotlin source
val compileKotlin: KotlinCompile by tasks
val compileJava: JavaCompile by tasks
compileJava.destinationDirectory.set(compileKotlin.destinationDirectory)

// pull all dependencies from here
repositories {
    mavenCentral()
}

// libraries that we need, using versions from Version Catalog
// we also want to use the shared/ library, so we need to include it here
dependencies {
    implementation(project(":shared"))
    implementation(libs.kotlin.coroutines)
    testImplementation(libs.junit.jupiter)
}

// fancy way of saying "use JUnit 5"
tasks.test {
    useJUnitPlatform()
}

// tell Gradle to use a specific version of the JDK when compiling
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(libs.versions.jdk.get()))
    }
}

// application plugin settings
// module name that we've set in the module-info.java
// fully-qualified classname to excecute when you use "gradle run"
application {
    mainModule.set("net.codebot.application")
    mainClass.set("net.codebot.application.Main")
}

// JavaFX plugin settings
// tell the JavaFX plugin which modules to include
// you may need to add others e.g. javafx.web
javafx {
    version = libs.versions.javafx.get()
    modules = listOf("javafx.controls", "javafx.graphics")
}

// get around small output bug
// https://stackoverflow.com/questions/74453018/jlink-package-kotlin-in-both-merged-module-and-kotlin-stdlib
jlink {
    forceMerge("kotlin")
}

Console Application

The Console application has a similar structure to the Application project, with a single src folder and a build.gradle.kts file for this project. This is a console application so it doesn’t need JavaFX support.

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

// notice the syntax for the plugin section uses alias
// this is how we pull in the plugins from the Version Catalog (above)
// e.g. libs.plugins.kotlin.lang inserts the kotlin-lang plugin details.
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    application
    alias(libs.plugins.kotlin.lang)
    alias(libs.plugins.javamodularity)
}

// used for packaging only
group = "net.codebot"
version = "1.0.0"

// telling Gradle to put Java and Kotlin output in the same build structure
// required since we have Java files (module-info.java) and Kotlin source
val compileKotlin: KotlinCompile by tasks
val compileJava: JavaCompile by tasks
compileJava.destinationDirectory.set(compileKotlin.destinationDirectory)

// pull all dependencies from here
repositories {
    mavenCentral()
}

// libraries that we need, using versions from Version Catalog
// we also want to use the shared/ library, so we need to include it here
dependencies {
    implementation(project(":shared"))
    testImplementation(libs.junit.jupiter)
}

// fancy way of saying "use JUnit 5"
tasks.test {
    useJUnitPlatform()
}

// tell Gradle to use a specific version of the JDK when compiling
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(libs.versions.jdk.get()))
    }
}

// application plugin settings
// module name that we've set in the module-info.java
// fully-qualified classname to excecute when you use "gradle run"
application {
    mainModule.set("net.codebot.console")
    mainClass.set("net.codebot.console.MainKt")
}

Shared Project

Finally, the Shared project provides services to both the Application and Console projects. It’s basically a library - not something that we execute directly, but code that we need to pull into the other projects. We keep it in a shared project to avoid code duplication.

Here’s the relevant build.gradle.kts.

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

// notice the syntax for the plugin section uses alias
// this is how we pull in the plugins from the Version Catalog (above)
// e.g. libs.plugins.kotlin.lang inserts the kotlin-lang plugin details.
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    `java-library`
    alias(libs.plugins.kotlin.lang)
    alias(libs.plugins.javamodularity)
}

// used for packaging only
group = "net.codebot"
version = "1.0.0"

// telling Gradle to put Java and Kotlin output in the same build structure
// required since we have Java files (module-info.java) and Kotlin source
val compileKotlin: KotlinCompile by tasks
val compileJava: JavaCompile by tasks
compileJava.destinationDirectory.set(compileKotlin.destinationDirectory)

// pull all dependencies from here
repositories {
    mavenCentral()
}

// libraries that we need, using versions from Version Catalog
// notice that the shared project needs sqlite and exposed, since 
// it manages a database that the other projects use (indirectly).
dependencies {
    implementation(libs.sqlite)
    implementation(libs.exposed.core)
    implementation(libs.exposed.jdbc)
    implementation(libs.sl4j.api)
    implementation(libs.sl4j.simple)
    testImplementation(libs.junit.jupiter)
}

// fancy way of saying "use JUnit 5"
tasks.test {
    useJUnitPlatform()
}

// tell Gradle to use a specific version of the JDK when compiling
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(libs.versions.jdk.get()))
    }
}

For details on module-setup for each of these projects, refer to packages and modules