build.gradle.kts
// empty file
settings.gradle.kts
rootProject.name = "kotlin-everywhere"
build.gradle.kts
tasks.register("t1") {
inputs.dir("...")
ouptuts.file("...")
doLast {
...
}
}
settings.gradle.kts
rootProject.name = "kotlin-everywhere"
build.gradle.kts
tasks.register("t1") {
inputs.dir("...")
ouptuts.file("...")
doLast {
// do some work
}
}
settings.gradle.kts
rootProject.name = "kotlin-everywhere"
buildSrc/src/main/kotlin/MyProject.kt
fun org.gradle.api.Project.configureMyProject() {
tasks.register("t1") {
inputs.dir("...")
ouptuts.file("...")
doLast {
// do some work
}
}
}
build.gradle.kts
configureMyProject()
You define a model of your software project through a Java/Kotlin API
Entry point: org.gradle.api.Project
You define a model of your software project through a Java/Kotlin API
Entry point: org.gradle.api.Project
fun org.gradle.api.Project.configureMyProject() {
println("Tasks: ${tasks.size}")
plugins.apply("java")
println("Tasks: ${tasks.size} (Java plugin added)")
plugins.apply("org.jetbrains.kotlin.jvm")
println("Tasks: ${tasks.size} (Kotlin plugin added)")
plugins.apply("com.android.application")
println("Tasks: ${tasks.size} (Android plugin added)")
}
> Configure project :
Tasks: 16
Tasks: 31 (Java plugin added)
Tasks: 36 (Kotlin plugin added)
Tasks: 49 (Android plugin added)
You define a model of your software project through a Java/Kotlin API
Entry point: org.gradle.api.Project
You define a model of your software project through a Java/Kotlin API
Entry point: org.gradle.api.Project
settings.gradle.kts
include("user-data", "account-data", "services", "desktop-app", "android-app")
project(":user-data").plugins.apply("java")
project(":account-data").plugins.apply("java")
project(":services").plugins.apply("org.jetbrains.kotlin.jvm")
project(":desktop-app").plugins.apply("org.jetbrains.kotlin.jvm")
project(":android-app").plugins.apply("org.jetbrains.kotlin.android")
project(":android-app").plugins.apply("com.android.application")
settings.gradle.kts
include("user-data", "account-data", "services", "desktop-app", "android-app")
rootProject.children.forEach {
val base = when(it.name) {
"user-data" -> "data"
"account-data" -> "data"
"services" -> "services"
else -> "apps"
}
it.projectDir = file("$base/${it.name}")
}
Entry point: org.gradle.api.artifacts.Configuration
(API for 3 concepts)
Provide: define sets of artifacts I produce for other projects
Consume: define sets of dependencies to projects and modules
Resolve: tool to collect artifacts from dependencies (e.g. all jars)
Provide: define sets of artifacts I produce for other projects (:services)
val everythingYouNeedToRunMe by configurations.creating {
isCanBeResolved = false; isCanBeConsumed = true // this is a variant!
}
Provide: define sets of artifacts I produce for other projects (:services)
val everythingYouNeedToRunMe by configurations.creating {
isCanBeResolved = false; isCanBeConsumed = true // this is a variant!
extendsFrom(otherComponentsINeedToRun)
}
Consume: define sets of dependencies to projects and modules (:desktop-app)
val otherComponentsINeedToRun by configurations.creating {
isCanBeResolved = false; isCanBeConsumed = false // this is a bucket!
}
dependencies {
otherComponentsINeedToRun(project(":services"))
}
Resolve: tool to collect artifacts from dependencies (e.g. all jars)
val pathToAllFiles by configurations.creating {
isCanBeResolved = true; isCanBeConsumed = false // this is a tool!
extendsFrom(otherComponentsINeedToRun)
}
tasks.register("allFiles") {
doLast {
println(configurations["pathToAllFiles"].incoming
.artifacts.artifactFiles.map { it.name })
}
}
gradlew desktop-app:allFiles
> Task :desktop-app:allFiles
[services.jar, services-sources.jar]
desktop-app/build.gradle.kts
dependencies {
implementation(project(":services"))
}
services/build.gradle.kts
dependencies {
api(project(":user-data"))
implementation("com.conversantmedia:disruptor:1.2.15")
}
gradlew desktop-app:dependencies --configuration compileClasspath
compileClasspath
\--- project :services
\--- project :user-data
gradlew desktop-app:dependencies --configuration compileClasspath
compileClasspath
\--- project :services
\--- project :user-data
gradlew desktop-app:dependencies --configuration runtimeClasspath
runtimeClasspath
\--- project :services
+--- project :user-data
\--- com.conversantmedia:disruptor:1.2.15
\--- org.slf4j:slf4j-api:1.7.13
gradlew services:outgoingVariants
Variant api-variant
--------------------------------------------------
Attributes
- org.gradle.jvm.version = 11
- org.gradle.libraryelements = classes
- org.gradle.usage = java-api
desktop-app/build.gradle.kts
configurations["compileClasspath"].attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
}
desktop-app/build.gradle.kts
configurations["compileClasspath"].attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
}
gradlew desktop-app:dependencies --configuration compileClasspath
compileClasspath
\--- project :services
+--- project :user-data
\--- com.conversantmedia:disruptor:1.2.15
\--- org.slf4j:slf4j-api:1.7.13
desktop-app/build.gradle.kts
configurations["runtimeClasspath"].attributes {
}
gradlew desktop-app:allFiles
> Task :desktop-app:allFiles
[services.jar, user-data.jar, disruptor-1.2.15.jar,
slf4j-api-1.7.13.jar]
desktop-app/build.gradle.kts
configurations["runtimeClasspath"].attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8)
}
gradlew desktop-app:allFiles
> Task :desktop-app:allFiles
[services-jdk8.jar, user-data-jdk8.jar,
disruptor-1.2.15.jar, slf4j-api-1.7.13.jar]
For projects: Gradle has the full model in memory
For modules: Gradle needs to build the model from metadata
com/conversantmedia/disruptor/1.2.15/disruptor-1.2.15.pom
<groupId>com.conversantmedia</groupId>
<artifactId>disruptor</artifactId>
<packaging>jar</packaging>
<version>1.2.15</version>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.13</version>
<scope>compile</scope>
</dependency>
com/conversantmedia/disruptor/1.2.15/*.jar
disruptor-1.2.15-jdk10.jar 2018-12-20 14:41 134319
disruptor-1.2.15-jdk8.jar 2018-12-20 14:41 134482
disruptor-1.2.15.jar 2018-12-20 14:41 134482
disruptor-1.2.15.module
"component": { "group": "com.conversantmedia", "module": "disruptor", "version": "1.2.15" },
"variants": [
{ "name": "runtimeElements",
"attributes": { "org.gradle.jvm.version": 11, "org.gradle.usage": "java-runtime" },
"dependencies": [{ "group": "org.slf4j", "module": "slf4j-api", "version": { "requires": "1.7.13" }}],
"files": [{ "name": "conversantmedia-1.2.15.jar", "url": "conversantmedia-1.2.15.jar" }] },
{ "name": "jdk8RuntimeElements",
"attributes": { "org.gradle.jvm.version": 8, "org.gradle.usage": "java-runtime" },
"dependencies": [{ "group": "org.slf4j", "module": "slf4j-api", "version": { "requires": "1.7.13" }}],
"files": [{ "name": "conversantmedia-1.2.15-jdk8.jar", "url": "conversantmedia-1.2.15-jdk8.jar" }]
},
desktop-app/build.gradle.kts
configurations["runtimeClasspath"].attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8)
}
gradlew desktop-app:allFiles
> Task :desktop-app:allFiles
[services-jdk8.jar, user-data-jdk8.jar,
disruptor-1.2.15-jdk8.jar, slf4j-api-1.7.13.jar]
dependencies {
implementation("velocity:velocity:1.5")
implementation("org.slf4j:log4j-over-slf4j:1.7.10")
}
Capabilities: Provided by variants, each can be provided only once
Attributes: Define which implementation of a capability fits best
dependencies {
implementation("velocity:velocity:1.5")
implementation("org.slf4j:log4j-over-slf4j:1.7.10")
}
Capabilities: Provided by variants, each can be provided only once
Attributes: Define which implementation of a capability fits best
dependencies {
implementation("velocity:velocity:1.5")
implementation("org.slf4j:log4j-over-slf4j:1.7.10")
}
Cannot select module with conflict on capability 'log4j:log4j:1.2.12'
also provided by [org.slf4j:log4j-over-slf4j:1.7.13(api-variant)]
class LoggingCapability : ComponentMetadataRule {
val loggingModules = setOf("log4j", "log4j-over-slf4j")
override fun execute(ctx: ComponentMetadataContext) = ctx.details.run {
if (loggingModules.contains(id.name)) {
allVariants {
withCapabilities { addCapability("log4j", "log4j", id.version) }
}
}
}
}
Component Metadata Rules: Add missing metadata
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("log4j:log4j") {
select(candidates.find { it.module == "log4j-over-slf4j" })
because("use slf4j in place of log4j")
}
}
Capability Resolution Strategy: Decide what fits in your context
user-data/src/main/java/com/acme/User.kt
data class User(val firstName: String, val lastName: String)
user-data/src/testFixtures/java/com/acme/Simpsons.kt
object Simpsons {
val HOMER = User("Homer", "Simpson")
val MARGE = User("Majorie", "Simpson")
val BART = User("Bartholomew", "Simpson")
val LISA = User("Elisabeth Marie", "Simpson")
val MAGGIE = User("Margaret Eve", "Simpson")
val FAMILY = setOf(HOMER, MARGE, BART, LISA, MAGGIE)
}
user-data/build.gradle.kts
plugins {
`java-library`
`java-test-fixtures` // new plugin since Gradle 5.6
`maven-publish` // you can publish test fixtures with GMM!
}
dependencies {
api(project(":user-data"))
implementation("com.conversantmedia:disruptor:1.2.15")
testImplementation(testFixtures(project(":user-data")))
testImplementation(testFixtures("com.conversantmedia:disruptor:1.2.15"))
}
For projects: Gradle always uses/builds the working copy
For modules: Gradle needs to choose a version
Gradle considers all version constraints in the dependency graph
dependencies {
implementation("com.conversantmedia:disruptor:1.2.15")
api("org.slf4j:slf4j-api") {
version { requires("1.6.6") }
}
}
dependencies {
implementation("com.conversantmedia:disruptor:1.2.15")
constraints {
api("org.slf4j:slf4j-api") {
version {
requires("1.6.6")
}
}
}
}
dependencies {
implementation("com.conversantmedia:disruptor:1.2.15")
constraints {
api("org.slf4j:slf4j-api") {
version {
requires("1.6.6")
forSubgraph()
}
}
}
}
plugins { `java-platform` }
dependencies {
constraints {
api("com.conversantmedia:disruptor:1.2.15")
api("org.slf4j:slf4j-api:1.6.6") { version { forSubgraph() } }
}
}
dependencies {
api(platform(project(":my-platform")))
implementation("com.conversantmedia:disruptor")
}
With Gradle you model your project through a Java/Kotlin API
org.gradle.api.Project
(basic building blocks, apply plugins here)
org.gradle.api.Task
(not covered today, let plugins add them)
org.gradle.api.artifacts.Configuration
(good stuff is hidden here)
Use variants as interface between projects and modules
Variant Attributes and Capabilities 😳
Gradle 6.0 will go full steam on Gradle Module Metadata (GMM) 🚀
All new features shown are compatible with GMM 🥳
Configuration is the entry point (better APIs will follow) 🙈
🕊️ @jeoj