Personally I would try to reuse only complex parts, or complex tasks/plugins if really needed. There is a maintenance cost with "parent logic" especially for things that change often and a lot flexibility can be lost. Also updating multiple dependent projects is not fun. So be careful.
With Gradle it's possible to reuse some common build logic. One way of doing this is to create a convention Gradle project. I will use Kotlin dsl in examples, but same thing can be done with groovy.
Convention project
First create a normal Gradle project and put in build.gradle.kts config like:
plugins {
// Kotlin dsl plugin since we will use Kotlin dsl
// (you can also use Groovy version if you like Groovy)
`kotlin-dsl`
// Plugin needed to publish it
id("maven-publish")
}
repositories {
// This repository is needed for getting kotlin-gradle-plugin,
// you can also add any other repo here if you add any other dep.
gradlePluginPortal()
}
dependencies {
// This is needed so we can access gradle constructs
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30")
}
publishing {
// Config to publish it
publications {
create<MavenPublication>("maven") {
group = "com.mycompany"
artifactId = "gradle-conventions"
version = "1.0.0"
from(components["java"])
}
}
repositories {
// my repositories where I want publish this
}
}
Where to put common logic? You put it in src/main/kotlin
(or groovy if you use groovy). So lets create such structure:
└── src
└── main
└── kotlin
├── dependencies
│ └── CommonDependencies.kt
├── my-company.java-conventions.gradle.kts
Where CommonDependenies.kt
has our dependencies:
package dependencies
open class CommonDependencies {
val guava = "com.google.guava:guava:30.1.1-jre"
}
and my-company.java-conventions.gradle.kts
has our common Java settings:
import dependencies.CommonDependencies
plugins {
id("java-library")
}
// register extension so we can nicely access variables from CommonDependencies
extensions.create<CommonDependencies>("commonLibs")
java {
// All our projects will use toolchains with Java11
toolchain.languageVersion.set(JavaLanguageVersion.of("11"))
}
tasks.test {
// All our projects use Junit
useJUnitPlatform()
}
Now since this is a regular project you can publish it to maven repo. For testing purposes, let's publish it to local maven repo with:
./gradlew publishToMavenLocal
Ok, our conventions are all set. Now how we can use them?
Consumer project
In consumer project we have to add our project to build logic classpath. This can be done by adding our project to buildSrc/build.gradle(.kts)
or to buildScript. Lets for example put it into buildSrc.
buildSrc/build.gradle.kts
example:
repositories {
// I have put maven local here just because I published
// convention project to maven local
mavenLocal()
gradlePluginPortal()
}
dependencies {
implementation("com.mycompany:gradle-conventions:1.0.0")
}
And after this is set, IDE reloaded, you can use your conventions in your modules. Example:
plugins {
id("my-company.java-conventions")
}
dependencies {
implementation(commonLibs.guava)
}
Notes
- If you don't want to publish your conventions to some Maven repo, you can also just include project locally with
includeBuild()
. For example if you have projects in same folders like that:
├── gradle-conventions
├── gradle-project-consuming-conventions
You would do in settings.gradle(.kts) of gradle-project-consuming-convention: includeBuild("../gradle-conventions")
In my-company.java-conventions.gradle.kts
you can skip my-company
. Name it however you think is best, just be careful that it does not conflict with official plugins.
I used Gradle 7.2