Fully remote distributed company
Open Source (APL)
for Java, Groovy, Kotlin, Scala, …
Developed by Gradle Inc. with many community contributions
Works with multiple Built Tools: Gradle Build Tool, Maven, …
Build Insights, Caching, Performance Analysis, Flaky Test Detection, …
Public Build Scans for Gradle builds: scans.gradle.com
Can be used to write imperative scripts
like Ant, but with a proper programming language
Can be used to model your software
like Maven, but with a richer and more extensible model
Many Gradle builds today are a mix of both
Its strength is its weakness: spaghetti code build scripts are common :(
Look at Gradle as a tool to model your software
Let Gradle read that model to build your software
Even if you are new to Gradle, you can and should start this way!
You write "build scripts", which should be models of your software
Use Gradle Groovy DSL (Domain Specific Language)
Or Gradle Kotlin DSL (Domain Specific Language)
Gradle runs on the JVM and compiles to JVM bytecode. Other JVM language can also be used to control Gradle. |
A set of core systems which are language-agnostic
Execution engine, dependency management, caching, …
Everything else is a Plugin
Core plugins: Java, Groovy, Scala, …
Community plugins: Kotlin (by JetBrains), Android (by Google), …
Your own plugins as part of your software!
Demo #1
We define each component in an independent Gradle build
─ domain-model
└── settings.gradle.kts
─ server-application
└── settings.gradle.kts
─ user-feature
└── settings.gradle.kts
─ user-feature
├── data
│ └─ build.gradle.kts
│ | plugins { id("java-library") } // generic type (core plugin)
│ | java { toolchain { ... } } // inidvidual configuration
│ | ...
│
├── table
│ └─ build.gradle.kts
│ | plugins { id("java-library") } // generic type (core plugin)
│ | java { toolchain { ... } } // inidvidual configuration
│ | ...
│
└── settings.gradle.kts
| include("data", "table") // inner component structure
Demo #2 - We add a separate component (Gradle build) for build logic
- build-logic
└── settings.gradle.kts
- domain-model
└── settings.gradle.kts
- server-application
└── settings.gradle.kts
- user-feature
└── settings.gradle.kts
─ user-feature
├── data
│ └── build.gradle.kts
│ | plugins { id("com.example.java-library") } // project type
│
├── table
│ └── build.gradle.kts
│ | plugins { id("com.example.java-library") } // project type
│
└── settings.gradle.kts
| includeBuild("../build-logic") // location of a source component
| repositories { mavenCentral() } // location of binary components
| include("data", "table") // inner structure
─ build-logic
├── java-library
│ └── build.gradle.kts
│ | plugins { `kotlin-dsl` } // project type for Gradle plugins
│
├── kotlin-library
│ └── build.gradle.kts
│ | plugins { `kotlin-dsl` } // project type for Gradle plugins
│
└── settings.gradle.kts
| // includeBuild(..) // location source component
| repositories { gradlePluginPortal() } // location binary components
| include("java-library", "kotlin-library") // inner structure
Demo #3 - We add dependencies between our components
- build-logic
└── settings.gradle.kts
- domain-model
└── settings.gradle.kts
- server-application
└── settings.gradle.kts
- user-feature
└── settings.gradle.kts
─ user-feature
├── data
│ └── build.gradle.kts
│ | plugins { id("com.example.java-library") } // project type
│ | dependencies { api("com.example.myproduct:release") }
│
├── table
│ └── build.gradle.kts
│ | plugins { id("com.example.java-library") } // project type
│ | dependencies { ... }
│
└── settings.gradle.kts
| includeBuild("../build-logic") // location of a source component
| includeBuild("../domain-model") // location of a source component
| repositories { mavenCentral() } // location of binary components
| include("data", "table") // inner structure
Gradle knows inputs and outputs
If output of a task is the input of another, there is a dependency
Incremental - a task only executes if input/output changes
Build cache - Outputs can be retrieved from (remote) cache
Demo #4
Exploring: task execution, incremental builds, build cache, custom tasks
An abstract class extending DefaultTask
Written in Java, Scala, Groovy, Kotlin
Properties as annotated abstract getter methods (getPropertyName()
)
Each task has an action implemented in a @TaskAction
method
Look at Gradle as a tool to model your software
Treat each component in your architecture as a separate Gradle build
Treat build configuration and customization as separate components
Gradle Manual: Structuring and Building a Software Product with Gradle
Ask questions in forums: discuss.gradle.org/
Report issues: github.com/gradle/gradle
Reach me on Twitter: @jeoj
Each project (or binary component) has multiple variants
Gradle selects one variant during dependency resolution
For example: Java Libraries have an "API" and a "Runtime" variant
Publish any project as binary component with maven-publish
plugin
Published to repository (can be a local folder)
─ user-feature
└── settings.gradle.kts
| // includeBuild("domain-model") // find here ⤵️ instead
| repositories { maven { url = uri("my-repository") } }
Tasks implementation (imperative code) in build scripts
Don’t access state from other projects
project(":table").tasks.jar.archiveFile
← don’t do this
Using a "root project" build.gradle(.kts)
to share build logic
You need to keep care of order yourself (when is a plugin applied exactly?)
Mix of concerns (one big if/else block)
Can’t access plugin extensions easily
allprojects { // Don't do this!
if (plugins.hasPlugin("java-library")) { ... }
if (plugins.hasPlugin("org.jetbrains.kotlin.jvm")) { ... }
}
If you need to run "clean" to trust your build…
…you may have imperative logic outside tasks (or transforms)
…a custom task implementation is broken