Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Everything you didn't want to know about the Kotlin DSL

415051ecb98d3a6aceb79e24c0fad091?s=47 mbonnin
December 04, 2021

Everything you didn't want to know about the Kotlin DSL

415051ecb98d3a6aceb79e24c0fad091?s=128

mbonnin

December 04, 2021
Tweet

Transcript

  1. Gradle - Kotlin Everything you didn’t want to know about

    the Gradle Kotlin APIs
  2. @MartinBonnin apollographql/apollo-android

  3. Disclaimer

  4. Once upon a time...

  5. 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 $@
  6. Maven 2004 <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion> <groupId>com.apollographql.federation</groupId> <artifactId>federation-parent</artifactId> <version>0.7.1-SNAPSHOT</version> <packaging>pom</packaging> <dependencyManagement> <dependencies> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>${graphql-java.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>com.diffplug.spotless</groupId> <artifactId>spotless-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
  7. 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') } } }
  8. Kotlin DSL 2018

  9. Differences

  10. 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' } } }
  11. 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” } } }
  12. 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' } } }
  13. 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
  14. 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' } }
  15. How does it work? With magic ✨

  16. How does it work? With magic autogenerated accessors✨

  17. 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
  18. The good news

  19. • Project • Plugins • Tasks • Extension • SourceSets

    • Configurations The Gradle Model https://github.com/autonomousapps/gradle-glossary
  20. 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
  21. Extension https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtensionAware.html

  22. 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
  23. // When you're writing kotlin { explicitApi() }

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

    you're really doing is project.extensions.getByName("kotlin").apply { this as KotlinJvmProjectExtension explicitApi() }
  25. // 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<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension>): Unit = (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kotlin", configure)
  26. // You could also write configure<KotlinJvmProjectExtension> { explicitApi() } //

    Or use 'the' the<KotlinJvmProjectExtension>().apply { explicitApi() } // Even in imperative style, without lambda the<KotlinJvmProjectExtension>().explicitApi() // It all works!
  27. Configuration https://github.com/gradle/gradle/blob/master/subprojects/dependency-management/src/main/java/or g/gradle/internal/component/external/descriptor/Configuration.java

  28. 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
  29. Configuration https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html

  30. 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
  31. Configuration A list of dependencies Identified by “group:artifact:version” “implementation” is

    the name of a Configuration “api” too
  32. // When you’re writing dependencies { implementation("com.squareup.okio:okio:3.0.0") }

  33. // 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")) }
  34. // 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)
  35. // 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...
  36. 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
  37. 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
  38. // When you're writing tasks { compileKotlin { kotlinOptions {

    apiVersion = "1.3" } } }
  39. // 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" } }
  40. // 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<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> get() = named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileKotlin")
  41. Gradle Containers • project.configurations • project.extensions • project.tasks • project.sourceSets

    • NamedDomainObjectContainer
  42. When are accessors generated?

  43. 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 ✨ ✨
  44. class KotlinPlugin: Plugin<Project> { 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
  45. 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

  46. 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
  47. 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<KotlinJvmProjectExtension> { explicitApi() } tasks.named("compileKotlin", KotlinCompile::class) { kotlinOptions { apiVersion = "1.3" } } } Kotlin build script
  48. Kotlin DSL != Kotlin build scripts

  49. Kotlin DSL ~= Kotlin build scripts + Generated Accessors +

    gradleKotlinDSL() + SamWithReceiver
  50. gradleKotlinDsl() dependency • the<KotlinJvmProjectExtension>() • val commonMain by getting() •

    val appleMain by creating() • val myProperty: String by project • closureOf<PackageConfig> {} • ...
  51. SamWithReceiver val taskProvider = tasks.named("compileKotlin", KotlinCompile::class) taskProvider.configure { kotlinOptions {

    apiVersion = "1.3" } }
  52. 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<T> { void execute(T t); }
  53. https://kotlinlang.org/docs/sam-with-receiver-plugin.html @HasImplicitReceiver public interface Action<T> { /** * Performs this

    action against the given object. * * @param t The object to perform the action on. */ void execute(T t); }
  54. Should I care?

  55. None
  56. None
  57. https://github.com/gradle/gradle/issues/15886#issuecomment-954833377

  58. “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”
  59. 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/
  60. • Kotlin DSL != Kotlin build scripts • Use convention

    plugins for your Kotlin build logic: https://developer.squareup.com/blog/herding-elephants/ Takeaways
  61. Merci! @MartinBonnin 🎄 https://bit.ly/everything-you-didnt-want-to-know-about-gradle

  62. 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