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

Everything you didn't want to know about the Ko...

mbonnin
December 04, 2021

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

mbonnin

December 04, 2021
Tweet

More Decks by mbonnin

Other Decks in Programming

Transcript

  1. 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 $@
  2. 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>
  3. 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') } } }
  4. 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' } } }
  5. 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' } } }
  6. 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
  7. 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' } }
  8. 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
  9. • Project • Plugins • Tasks • Extension • SourceSets

    • Configurations The Gradle Model https://github.com/autonomousapps/gradle-glossary
  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” } } } Extension
  11. 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
  12. // When you're writing kotlin { explicitApi() } // What

    you're really doing is project.extensions.getByName("kotlin").apply { this as KotlinJvmProjectExtension explicitApi() }
  13. // 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)
  14. // 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!
  15. 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
  16. 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
  17. // 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")) }
  18. // 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)
  19. // 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...
  20. 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
  21. // 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" } }
  22. // 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")
  23. 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 ✨ ✨
  24. 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
  25. 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
  26. 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
  27. gradleKotlinDsl() dependency • the<KotlinJvmProjectExtension>() • val commonMain by getting() •

    val appleMain by creating() • val myProperty: String by project • closureOf<PackageConfig> {} • ...
  28. 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); }
  29. 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); }
  30. “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”
  31. 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/
  32. • Kotlin DSL != Kotlin build scripts • Use convention

    plugins for your Kotlin build logic: https://developer.squareup.com/blog/herding-elephants/ Takeaways