Paul Merlin (@eskat0s) & Rodrigo B. de Oliveira (@rodrigobamboo) - Gradle Inc.
Gradle Kotlin DSL Team
How are you using Gradle?
What is your primary programming language?
Gradle is a build and automation tool.
JVM based
Implemented in Java
100% Free Open Source - Apache Standard License 2.0
Java ecosystem
Groovy, Kotlin, Scala, …
Native ecosystem
C, C++, Swift, …
Android
Misc
Python, Go, Asciidoctor, …
Creating a Gradle Kotlin DSL build
Customizing the build
Organizing build logic
Authoring plugins and DSLs
Automate execution of configurable tasks
Groovy and Kotlin build scripts
A Java API with a Kotlin and Groovy DSL on top
Work avoidance, dependency management etc…
Reusable functionality packaged as plugins
plugins {
`java-library`
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
dependencies {
api("com.acme:foo:1.0")
implementation("com.zoo:monkey:1.1")
}
plugins {
`cpp-application`
}
application {
baseName = "my-app"
}
toolChains {
// ...
}
With Gradle locally installed
gradle init
Without Gradle locally installed
From a Web UI
One-liner without Gradle installed
➜ mkdir webinar-app \
&& cd webinar-app \
&& curl https://gradle-initializr.cleverapps.io/starter.zip \
-d type=kotlin-application \
-d dsl=kotlin \
-d projectName=webinar-app \
-d gradleVersion=5.3.1 \
-o webinar-app.zip \
&& unzip -o webinar-app.zip \
&& rm webinar-app.zip
.
├── settings.gradle.kts
├── build.gradle.kts
├── gradlew
├── gradlew.bat
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── src
├── main
│ └── kotlin
│ └── *
└── test
└── kotlin
└── *
.
├── settings.gradle.kts ⬅
├── build.gradle.kts
├── gradlew
├── gradlew.bat
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── src
├── main
│ └── kotlin
│ └── *
└── test
└── kotlin
└── *
settings.gradle.kts
rootProject.name = "webinar-app"
.
├── settings.gradle.kts
├── build.gradle.kts ⬅
├── gradlew
├── gradlew.bat
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── src
├── main
│ └── kotlin
│ └── *
└── test
└── kotlin
└── *
build.gradle.kts
plugins {
id("org.jetbrains.kotlin.jvm") version "1.3.21"
application
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClassName = "webinar.app.AppKt"
}
.
├── settings.gradle.kts
├── build.gradle.kts
├── gradlew ⬅
├── gradlew.bat ⬅
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar ⬅
│ └── gradle-wrapper.properties ⬅
└── src
├── main
│ └── kotlin
│ └── *
└── test
└── kotlin
└── *
.
├── settings.gradle.kts
├── build.gradle.kts
├── gradlew
├── gradlew.bat
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── src
├── main
│ └── kotlin
│ └── * ⬅
└── test
└── kotlin
└── * ⬅
From the command line
DEMO
Using an IDE
DEMO
build.gradle.kts
plugins {
id("org.jetbrains.kotlin.jvm") version "1.3.21"
application
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClassName = "webinar.app.AppKt"
}
application
pluginplugins {
id("org.jetbrains.kotlin.jvm") version "1.3.21"
application
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClassName = "webinar.app.AppKt"
applicationDefaultJvmArgs = listOf("-Xmx4m")
}
demo with ./gradlew run -i
highligh the new -Xmx4m
argument
demo ./gradlew install
open script and highlight new argument
plugins {
id("org.jetbrains.kotlin.jvm") version "1.3.21"
application
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClassName = "webinar.app.AppKt"
applicationDefaultJvmArgs = listOf("-Xmx4m")
}
plugins {
id("org.jetbrains.kotlin.jvm") version "1.3.21"
application
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClassName = "webinar.app.AppKt"
applicationDefaultJvmArgs = listOf("-Xmx4m")
}
tasks {
compileKotlin {
kotlinOptions { allWarningsAsErrors = true }
}
}
we can change the behavior of the build by
configuring extensions and tasks
both contributed by plugins
in other words, plugins extend the Gradle DSL which can then be configured in scripts like the one we just saw
DEMO
project evaluation order follows the hierarchy
script classpath is inherited
cross-configuration requires dynamic references
type-safe accessors are not always available
see the Kotlin DSL Primer in the User Manual for more information
Using non statically typed APIs and DSLs from a statically typed language
DEMO
you can mix Kotlin and Groovy scripts
when types are not available, withGroovyBuilder {}
to the rescue
see the Kotlin DSL Primer in the User Manual for more information
cross-configuration
buildSrc
sharing build logic between builds
buildSrc
.
├── buildSrc
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src
│ └── *
├── app
│ ├── build.gradle.kts
│ └── src
│ └── *
├── core
│ ├── build.gradle.kts
│ └── src
│ └── *
├── gradle
│ └── *
├── gradlew.bat
├── gradlew
├── build.gradle.kts
└── settings.gradle.kts
buildSrc
is the conventional build for build logic
the kotlin-dsl
plugin brings the Gradle Kotlin DSL support to Kotlin source sets
Gradle Kotlin DSL scripts in Kotlin source sets are exposed as Gradle plugins
see the Kotlin DSL Primer in the User Manual for more information
.
├── build-logic
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src
│ └── *
└── webinar-app
├── app
│ ├── build.gradle.kts
│ └── src
│ └── *
├── core
│ ├── build.gradle.kts
│ └── src
│ └── *
├── gradle
│ └── *
├── gradlew
├── gradlew.bat
├── build.gradle.kts
└── settings.gradle.kts
Sharing plugins with a wider audience
Publishing plugins
DSLs that play well with both Kotlin, Groovy and other JVM languages
Plugins that can be used across Gradle versions
To the Gradle Plugin Portal or an internal repository
Well covered in the Plugin Development guides
Doesn’t prevent the included build workflow
Which is orthogonal to choosing mono-repo vs. multi-repo
Diverse build script DSLs languages - Kotlin, Groovy
Diverse plugin implementation languages - any JVM language
Common denominator is static typing
Applicable to every JVM language
It’s always easier to use statically typed APIs from dynamically typed languages than the other way around
Prefer exposing statically typed APIs over dynamically or unityped APIs
enum class MyOption { NONE, FEW, LOTS, ALL }
// The good 👍
interface MyDslType {
fun doSomethingOn(option: MyOption)
// Optionally
fun doSomethingOn(option: String) =
doSomethingOn(MyOption.valueOf(option.toUpperCase()))
}
// The bad 👎
interface MyDslType {
fun doSomethingOn(option: Any)
}
Prefer Gradle types over language specific types
interface MyDslType {
// The good 👍
void doThis(org.gradle.api.Action<Type> block);
}
interface MyDslType {
// The bad 👎
fun doThis(block: Typed.() -> Unit)
}
interface MyDslType {
// The ugly 👹
def doThis(groovy.lang.Closure block)
}
Always start with / aim for the latest Gradle version
In order to take advantage of all the goodness
Apply best practices (see the Plugin Development guides)
Do cross-version testing
Can be as simple as JUnit parameterized tests driving TestKit
Cross-version testing with JUnit
abstract class AbstractPluginTest(val gradleVersion: String) {
companion object {
@Parameterized.Parameters(name = "Gradle {0}")
@JvmStatic
fun testedGradleVersions() = listOf("5.3.1", ...., "4.7") ⬅
}
fun gradleRunnerFor(vararg arguments: String) =
GradleRunner.create()
.withGradleVersion(gradleVersion) ⬅
.withPluginClasspath()
.withProjectDir(rootDir)
.withArguments(*arguments)
}
Incrementally support Gradle versions
Add Gradle version to the test matrix
Fix as needed (see the Upgrading Gradle section of the User Manual)
Repeat
Stop when satisfied or overwhelmed
Supporting multiple Gradle versions
val myTaskConfiguration: MyTask.() -> Unit = {
// ...
}
if (GradleVersion.current() >= GradleVersion.version("4.9")) {
// Support lazy task registration
val myTask = tasks.register("my", MyTask::class.java, myTaskConfiguration)
// ...
} else {
// Eager task creation
val myTask = tasks.create("my", MyTask::class.java, myTaskConfiguration)
// ...
}
Creating a Gradle Kotlin DSL build with or without Gradle installed
Customizing builds
by configuring extensions and tasks contributed by plugins
by mixing Kotlin and Groovy build logic
by modularizing into projects
Organizing build logic using cross-configuration, buildSrc
and shared plugins
Authoring plugins and DSLs
that play well with different DSLs and JVM languages
that support multiple Gradle versions