Slide 1

Slide 1 text

Gradle - Kotlin Everything you didn’t want to know about the Gradle Kotlin APIs

Slide 2

Slide 2 text

@MartinBonnin apollographql/apollo-android

Slide 3

Slide 3 text

Disclaimer

Slide 4

Slide 4 text

Once upon a time...

Slide 5

Slide 5 text

GNU Make 1988 CC= gcc # gcc or g++ CFLAGS=-g -Wall -DNORMALUNIX -DLINUX # -DUSEASM LDFLAGS=-L/usr/X11R6/lib LIBS=-lXext -lX11 -lnsl -lm # subdirectory for objects O=linux # not too sophisticated dependency OBJS= \ $(O)/doomdef.o \ $(O)/doomstat.o \ all: $(O)/linuxxdoom clean: rm -f *.o *~ *.flc rm -f linux/* $(O)/linuxxdoom: $(OBJS) $(O)/i_main.o $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(O)/i_main.o \ -o $(O)/linuxxdoom $(LIBS) $(O)/%.o: %.c $(CC) $(CFLAGS) -c $< -o $@

Slide 6

Slide 6 text

Maven 2004 4.0.0 com.apollographql.federation federation-parent 0.7.1-SNAPSHOT pom com.graphql-java graphql-java ${graphql-java.version} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} com.diffplug.spotless spotless-maven-plugin

Slide 7

Slide 7 text

Gradle 2008 plugins { id 'build-logic' id 'org.jetbrains.kotlin.jvm' version '1.5.31' } repositories { mavenCentral() } dependencies { implementation('com.squareup.okio:okio:3.0.0') } kotlin { sourceSets { main { languageSettings.optIn('kotlin.RequiresOptIn') } } }

Slide 8

Slide 8 text

Kotlin DSL 2018

Slide 9

Slide 9 text

Differences

Slide 10

Slide 10 text

plugins { id 'org.jetbrains.kotlin.jvm' version '1.5.31' } dependencies { implementation(’com.squareup.okio:okio:3.0.0’) } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = '1.3' } } }

Slide 11

Slide 11 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } }

Slide 12

Slide 12 text

plugins { id 'org.jetbrains.kotlin.jvm' version '1.5.31' } dependencies { implementation(’com.squareup.okio:okio:3.0.0’) } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = '1.3' } } }

Slide 13

Slide 13 text

plugins { id 'org.jetbrains.kotlin.jvm' version '1.5.31' } dependencies { implementation(’com.squareup.okio:okio:3.0.0’) } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = '1.3' } } } Dynamic Usages

Slide 14

Slide 14 text

Groovy metaprogramming https://groovy-lang.org/metaprogramming.html class SomeGroovyClass { def invokeMethod(String name, Object args) { return "called invokeMethod $name $args" } def test() { return 'method exists' } }

Slide 15

Slide 15 text

How does it work? With magic ✨

Slide 16

Slide 16 text

How does it work? With magic autogenerated accessors✨

Slide 17

Slide 17 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } } Configuration Extension Task

Slide 18

Slide 18 text

The good news

Slide 19

Slide 19 text

● Project ● Plugins ● Tasks ● Extension ● SourceSets ● Configurations The Gradle Model https://github.com/autonomousapps/gradle-glossary

Slide 20

Slide 20 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } } Extension

Slide 21

Slide 21 text

Extension https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtensionAware.html

Slide 22

Slide 22 text

Extension “Most plugins offer some configuration options for build scripts and other plugins to use to customize how the plugin works. Plugins do this using extension objects.” “An extension object is simply an object with Java Bean properties that represent the configuration.” https://docs.gradle.org/current/userguide/custom_plugins.html#sec:getting_input_from_the_build

Slide 23

Slide 23 text

// When you're writing kotlin { explicitApi() }

Slide 24

Slide 24 text

// When you're writing kotlin { explicitApi() } // What you're really doing is project.extensions.getByName("kotlin").apply { this as KotlinJvmProjectExtension explicitApi() }

Slide 25

Slide 25 text

// When you're writing kotlin { explicitApi() } // What you're really doing is project.extensions.getByName("kotlin").apply { this as KotlinJvmProjectExtension explicitApi() } // Because Gradle generated an accessor: fun org.gradle.api.Project.`kotlin`(configure: Action): Unit = (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kotlin", configure)

Slide 26

Slide 26 text

// You could also write configure { explicitApi() } // Or use 'the' the().apply { explicitApi() } // Even in imperative style, without lambda the().explicitApi() // It all works!

Slide 27

Slide 27 text

Configuration https://github.com/gradle/gradle/blob/master/subprojects/dependency-management/src/main/java/or g/gradle/internal/component/external/descriptor/Configuration.java

Slide 28

Slide 28 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } } Configuration

Slide 29

Slide 29 text

Configuration https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html

Slide 30

Slide 30 text

Configuration “A Configuration represents a group of artifacts and their dependencies” “Configuration is an instance of a FileCollection that contains all dependencies but not artifacts” “configurations have at least 3 different roles: - to declare dependencies - as a consumer, to resolve a set of dependencies to files - as a producer, to expose artifacts and their dependencies” https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html

Slide 31

Slide 31 text

Configuration A list of dependencies Identified by “group:artifact:version” “implementation” is the name of a Configuration “api” too

Slide 32

Slide 32 text

// When you’re writing dependencies { implementation("com.squareup.okio:okio:3.0.0") }

Slide 33

Slide 33 text

// When you’re writing dependencies { implementation("com.squareup.okio:okio:3.0.0") } // What you’re really doing is dependencies { project.configurations .getByName("implementation") .dependencies .add(create("com.squareup.okio:okio:3.0.0")) }

Slide 34

Slide 34 text

// When you’re writing dependencies { implementation("com.squareup.okio:okio:3.0.0") } // What you’re really doing is dependencies { project.configurations .getByName("implementation") .dependencies .add(create("com.squareup.okio:okio:3.0.0")) } // Because Gradle generated an accessor for you! fun DependencyHandler.`implementation`(dependencyNotation: Any): Dependency? = add("implementation", dependencyNotation)

Slide 35

Slide 35 text

// You could also write dependencies { "implementation"("com.squareup.okio:okio:3.0.0") } // Or that too dependencies { add("implementation", "com.squareup.okio:okio:3.0.0") } // It’s all the same...

Slide 36

Slide 36 text

Task “A Task represents a single atomic piece of work for a build, such as compiling classes or generating javadoc.” https://docs.gradle.org/current/dsl/org.gradle.api.Task.html

Slide 37

Slide 37 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } } Task

Slide 38

Slide 38 text

// When you're writing tasks { compileKotlin { kotlinOptions { apiVersion = "1.3" } } }

Slide 39

Slide 39 text

// When you're writing tasks { compileKotlin { kotlinOptions { apiVersion = "1.3" } } } // What you’re really doing is project.tasks.named("compileKotlin", KotlinCompile::class).configure { kotlinOptions { apiVersion = "1.3" } }

Slide 40

Slide 40 text

// When you're writing tasks { compileKotlin { kotlinOptions { apiVersion = "1.3" } } } // What you’re really doing is project.tasks.named("compileKotlin", KotlinCompile::class).configure { kotlinOptions { apiVersion = "1.3" } } // Because Gradle generated an accessor for you val TaskContainer.`compileKotlin`: TaskProvider get() = named("compileKotlin")

Slide 41

Slide 41 text

Gradle Containers ● project.configurations ● project.extensions ● project.tasks ● project.sourceSets ● NamedDomainObjectContainer

Slide 42

Slide 42 text

When are accessors generated?

Slide 43

Slide 43 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } // Accessors are generated here dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } Magic happens here ✨ ✨

Slide 44

Slide 44 text

class KotlinPlugin: Plugin { override fun apply(target: Project) { target.configurations.create("kotlinCompilerClasspath") target.extensions.create("kotlin", KotlinJvmProjectExtension::class.java) target.tasks.register("compileKotlin", CompileKotlinTask::class.java) } } abstract class CompileKotlinTask: DefaultTask() { // } abstract class KotlinJvmProjectExtension { // } Pseudo Kotlin Plugin Code

Slide 45

Slide 45 text

Accessors generation https://github.com/gradle/gradle/blob/master/subprojects/kotlin-dsl-provider-plugins/src/main/k otlin/org/gradle/kotlin/dsl/provider/plugins/DefaultProjectSchemaProvider.kt

Slide 46

Slide 46 text

plugins { id(”org.jetbrains.kotlin.jvm”).version(“1.5.31”) } // Accessors are generated here dependencies { implementation("com.squareup.okio:okio:3.0.0") } kotlin { explicitApi() } tasks { compileKotlin { kotlinOptions { apiVersion = “1.3” } } } Kotlin build script

Slide 47

Slide 47 text

buildscript { dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31") } } apply(plugin = "org.jetbrains.kotlin.jvm") dependencies { add("implementation", "com.squareup.okio:okio:3.0.0") } configure { explicitApi() } tasks.named("compileKotlin", KotlinCompile::class) { kotlinOptions { apiVersion = "1.3" } } } Kotlin build script

Slide 48

Slide 48 text

Kotlin DSL != Kotlin build scripts

Slide 49

Slide 49 text

Kotlin DSL ~= Kotlin build scripts + Generated Accessors + gradleKotlinDSL() + SamWithReceiver

Slide 50

Slide 50 text

gradleKotlinDsl() dependency ● the() ● val commonMain by getting() ● val appleMain by creating() ● val myProperty: String by project ● closureOf {} ● ...

Slide 51

Slide 51 text

SamWithReceiver val taskProvider = tasks.named("compileKotlin", KotlinCompile::class) taskProvider.configure { kotlinOptions { apiVersion = "1.3" } }

Slide 52

Slide 52 text

SamWithReceiver val taskProvider = tasks.named("compileKotlin", KotlinCompile::class) taskProvider.configure { // How come the task is in 'this' and not 'it' ?? kotlinOptions { apiVersion = "1.3" } } // configure takes an Action public interface Action { void execute(T t); }

Slide 53

Slide 53 text

https://kotlinlang.org/docs/sam-with-receiver-plugin.html @HasImplicitReceiver public interface Action { /** * Performs this action against the given object. * * @param t The object to perform the action on. */ void execute(T t); }

Slide 54

Slide 54 text

Should I care?

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

https://github.com/gradle/gradle/issues/15886#issuecomment-954833377

Slide 58

Slide 58 text

“I can copy paste and it just works” “Yay, now I understand what’s going on!” “Well, maybe I can sacrifice type safety for build speed”

Slide 59

Slide 59 text

apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { // lots of boilerplate } tasks.withType(KotlinCompile).configureEach { kotlinOptions { jvmTarget = JavaVersion.VERSION_11 } } Convention plugins plugins { id 'com.squareup.android.lib' } https://developer.squareup.com/blog/herding-elephants/

Slide 60

Slide 60 text

● Kotlin DSL != Kotlin build scripts ● Use convention plugins for your Kotlin build logic: https://developer.squareup.com/blog/herding-elephants/ Takeaways

Slide 61

Slide 61 text

Merci! @MartinBonnin 🎄 https://bit.ly/everything-you-didnt-want-to-know-about-gradle

Slide 62

Slide 62 text

Resources ● https://docs.gradle.org/current/userguide/kotlin _dsl.html ● https://blog.mbonnin.net ● https://www.youtube.com/watch?v=XXoIzzcJr80 ● https://github.com/bernaferrari/GradleKotlinCo nverter