Groovy to Kotlin in Buildscripts (Kotlin Night Berlin March 2018)

Groovy to Kotlin in Buildscripts (Kotlin Night Berlin March 2018)

Pros and cons of migrating build scripts from Groovy to Kotlin. Some examples and how the DSL works under the hood.

Talk recording:
https://www.youtube.com/watch?v=BAuP5Bfp-kk

5f69045a2ca496221cfc624405917cdf?s=128

Nelson Osacky

March 15, 2018
Tweet

Transcript

  1. Groovy to Kotlin in build scripts Nelson Osacky

  2. •Some Pros and Cons •Examples •How it works under the

    hood
  3. build.gradle -> build.gradle.kts

  4. Motivations

  5. Kotlin DSL v0.15.6

  6. Experiment

  7. Pros • Type safety • Auto complete • Kotlin Cons

    • Slower builds • Less examples • Pre-release state • Source digging
  8. type safety

  9. None
  10. if (isCi) { foo }A

  11. if (isCi) { foo }A

  12. auto-complete

  13. • autocompletion gif

  14. Slower builds

  15. None
  16. None
  17. Less docs https://github.com/gradle/kotlin-dsl/tree/master/samples

  18. Must dig in source

  19. Pros • type safety • better auto complete • kotlin

    Cons • slower builds • less examples • pre-release state • source digging
  20. Examples

  21. settings.gradle.kts

  22. include ':app' include ':data' include ':service'

  23. include( ":app", ":data", ":service" )

  24. build.gradle.kts

  25. plugins

  26. apply plugin: 'java-library' apply plugin: 'kotlin' apply plugin: 'com.android.library' targetCompatibility

    = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 dependencies { testCompile deps.junit compileOnly deps.support.annotations implementation deps.kotlin }
  27. apply plugin: 'java-library' apply plugin: 'kotlin' apply plugin: 'com.android.library'

  28. plugins { `java-library` kotlin("jvm") id("com.android.library") }A

  29. plugins { `java-library` kotlin("jvm") id("com.android.library") }A java { targetCompatibility =

    JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 }B dependencies { testImplementation(deps("junit")) compileOnly((deps("support") as Map<*, *>) ["annotations"].toString()) }C
  30. plugins { `java-library` kotlin("jvm") id("com.android.library") }A

  31. plugins { `java-library` kotlin("jvm") id("com.android.library") }A

  32. /** * Applies the given Kotlin plugin [module]. * *

    For example: `plugins { kotlin("jvm") version "1.2.21" }` * * @param module simple name of the Kotlin Gradle plugin module, for example "jvm", "android", "kapt", "plugin.allopen" etc... */ fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec = id("org.jetbrains.kotlin.$module") org.gradle.kotlin.dsl.KotlinDependencyExtensions.kt
  33. """ /** * Applies the given Kotlin plugin [module]. *

    * For example: `plugins { kotlin("jvm") version "$embeddedKotlinVersion" }` * * Visit the [plugin portal](https://plugins.gradle.org/search? term=org.jetbrains.kotlin) to see the list of available plugins. * * @param module simple name of the Kotlin Gradle plugin module, for example "jvm", "android", "kapt", "plugin.allopen" etc... */ fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec = id(“org.jetbrains.kotlin.${‘$’}module”) """ 
 buildSrc/src/main/kotlin/codegen/GenerateKotlinDependencyExtensions.kt
  34. val generateKotlinDependencyExtensions by task<GenerateKotlinDependencyExtensions> { val publishedPluginsVersion: String by rootProject.extra

    outputFile = File(apiExtensionsOutputDir, "org/gradle/kotlin/dsl/ KotlinDependencyExtensions.kt") embeddedKotlinVersion = kotlinVersion kotlinDslPluginsVersion = publishedPluginsVersion kotlinDslRepository = kotlinRepo } provider/build.gradle.kts
  35. plugins { `java-library` kotlin("jvm") id("com.android.library") }A

  36. plugins { `java-library` kotlin("jvm") id("com.android.library") }A

  37. /** * The builtin Gradle plugin implemented by [org.gradle.api.plugins.JavaLibraryPlugin]. */

    inline val PluginDependenciesSpec.`java-library`: PluginDependencySpec get() = id("org.gradle.java-library") BuiltInPluginExtensions.kt
  38. internal fun generateApiExtensionsJar(outputFile: File, gradleJars: Collection<File>, onProgress: () -> Unit)

    { ApiExtensionsJarGenerator(onProgress = onProgress).generate(outputFile, gradleJars) } org.gradle.kotlin.dsl.codegen.ApiExtensionsJar.kt
  39. apply plugin: 'java-library' apply plugin: 'kotlin' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility

    = JavaVersion.VERSION_1_8 dependencies { testImplementation 'junit:junit:4.12' compileOnly 'com.android.support:support- annotations:27.1.0' }A
  40. dependencies { testImplementation 'junit:junit:4.12' }A

  41. dependencies { testImplementation("junit:junit:4.12") }A

  42. plugins { `java-library` kotlin("jvm") } java { targetCompatibility = JavaVersion.VERSION_1_8

    sourceCompatibility = JavaVersion.VERSION_1_8 } dependencies { testImplementation("junit:junit:4.12") compileOnly("com.android.support:support- annotations:27.1.0") }A
  43. apply plugin: 'java-library' apply plugin: 'kotlin' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility

    = JavaVersion.VERSION_1_8 dependencies { testImplementation 'junit:junit:4.12' compileOnly 'com.android.support:support- annotations:27.1.0' }A
  44. plugins { `java-library` kotlin("jvm") } java { targetCompatibility = JavaVersion.VERSION_1_8

    sourceCompatibility = JavaVersion.VERSION_1_8 } dependencies { testImplementation("junit:junit:4.12") compileOnly("com.android.support:support- annotations:27.1.0") }A
  45. ext block

  46. ext.deps = [ 'junit' : "junit:junit:4.12", 'support' : [ 'annotations':

    "com.android.support:support-annotations:$ {versions.supportLibrary}", ], ]
  47. apply plugin: 'java-library' apply plugin: 'kotlin' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility

    = JavaVersion.VERSION_1_8 dependencies { testImplementation deps.junit compileOnly deps.support.annotations implementation deps.kotlin }W
  48. dependencies { testImplementation deps.junit compileOnly deps.support.annotations }W

  49. dependencies { testImplementation deps.junit }W

  50. dependencies { testImplementation(deps("junit")) }W

  51. dependencies { testImplementation deps.junit }Q 
 dependencies { testImplementation(deps("junit")) }W

  52. dependencies { testImplementation deps.junit }Q 
 dependencies { testImplementation(deps("junit")) }W

    fun Project.deps(key: String): Any { return (rootProject.ext["deps"] as Map<*, *>)[key]!! }D
  53. fun Project.deps(key: String): Any { return (rootProject.ext["deps"] as Map<*, *>)[key]!!

    }D
  54. fun Project.deps(key: String): Any { return (rootProject.ext["deps"] as Map<*, *>)[key]!!

    }D ext.deps = [ 'junit' : "junit:junit:4.12", 'support' : [ 'annotations': "com.android.support:support-annotations:$ {versions.supportLibrary}", ], ]
  55. fun Project.deps(key: String): Any { return (rootProject.ext["deps"] as Map<*, *>)[key]!!

    }D
  56. dependencies { testImplementation(deps(“junit")) compileOnly((deps("support") as Map<*, *>)["annotations"].toString()) }A fun Project.deps(key:

    String): Any { return (rootProject.ext["deps"] as Map<*, *>)[key]!! }D
  57. plugins { `java-library` kotlin("jvm") } java { targetCompatibility = JavaVersion.VERSION_1_8

    sourceCompatibility = JavaVersion.VERSION_1_8 }A dependencies { testImplementation(deps("junit")) compileOnly((deps("support") as Map<*, *>)["annotations"].toString()) implementation(deps("kotlin")) }
  58. apply plugin: 'java-library' apply plugin: 'kotlin' targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility

    = JavaVersion.VERSION_1_8 dependencies { testImplementation deps.junit compileOnly deps.support.annotations implementation deps.kotlin }W
  59. targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8

  60. java { targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 }A

  61. plugins { `java-library` kotlin("jvm") } java { targetCompatibility = JavaVersion.VERSION_1_8

    sourceCompatibility = JavaVersion.VERSION_1_8 }A dependencies { testImplementation(deps("junit")) compileOnly((deps("support") as Map<*, *>)["annotations"].toString()) implementation(deps("kotlin")) }
  62. // Groovy ext.isCi = System.getenv("CI") == "true"

  63. // Kotlin extra.set("isCi", System.getenv("CI") == "true")

  64. // Groovy ext.isCi = System.getenv("CI") == “true" // Kotlin extra.set("isCi",

    System.getenv("CI") == "true")
  65. val ExtensionAware.isCi : Boolean get() = extra.properties["isCi"].toString().toBoolean()

  66. // Groovy if (isCi) { // Do stuff on CI

    }B
  67. // Kotlin if (isCi) { // Do stuff on CI

    }B
  68. // Groovy if (isCi) { // Do stuff on CI

    }A // Kotlin if (isCi) { // Do stuff on CI }B
  69. // Groovy if (isCi) { // Do stuff on CI

    }A // Kotlin if (isCi()) { // Do stuff on CI }B val ExtensionAware.isCi : Boolean get() = extra.properties["isCi"].toString().toBoolean()
  70. • Experiments are fun • Build speeds are slower •

    Kotlin DSL is still pre-release • Everything is an extension function
  71. Thanks nelson.osacky@zenjob.com
 https://www.zenjob.de/careers/ Questions? https://github.com/gradle/kotlin-dsl/tree/master/samples

  72. targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8

  73. java { targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 }A

  74. public void setSourceCompatibility(Object value) { setSourceCompatibility(JavaVersion.toVersion(value)); } public void setSourceCompatibility(JavaVersion

    value) { srcCompat = value; } public void setTargetCompatibility(Object value) { setTargetCompatibility(JavaVersion.toVersion(value)); } public void setTargetCompatibility(JavaVersion value) { targetCompat = value; } org.gradle.api.JavaPluginConvention.java
  75. public void apply(ProjectInternal project) { project.getPluginManager().apply(JavaBasePlugin.class); JavaPluginConvention javaConvention = project.getConvention()

    .getPlugin(JavaPluginConvention.class); } org.gradle.api.plugins.JavaPlugin.java
  76. /** * Retrieves the [java][org.gradle.api.plugins.JavaPluginConvention] project convention. */ val Project.`java`:

    org.gradle.api.plugins.JavaPluginConvention get() = convention.getPluginByName<org.gradle.api.plugins.JavaPluginConvention>("java") /** * Configures the [java][org.gradle.api.plugins.JavaPluginConvention] project convention. */ fun Project.`java`(configure: org.gradle.api.plugins.JavaPluginConvention.() -> Unit): Unit = configure(`java`) org.gradle.kotlin.dsl.accessors.kt
  77. java { sourceCompatibility = JavaVersion.VERSION_1_8 } withConvention(JavaPluginConvention::class, { sourceCompatibility =

    JavaVersion.VERSION_1_8 }) (this as HasConvention).convention.getPlugin(JavaPluginConvention::class).run { sourceCompatibility = JavaVersion.VERSION_1_8 }
  78. // Groovy // Ensure the no-op leakcanary dependency is always

    used in JVM tests. configurations.all { config -> if (config.name.contains("UnitTest")) { config.resolutionStrategy.eachDependency { details -> if (details.requested.group == "com.squareup.leakcanary" && details.requested.name == "leakcanary-android") { details.useTarget(group: details.requested.group, name: "leakcanary-android-no-op", version: details.requested.version) }A }B }C }D
  79. // Kotlin // Ensure the no-op leakcanary dependency is always

    used in JVM tests. configurations.all { if (name.contains("UnitTest")) { resolutionStrategy.eachDependency { if (requested.group == "com.squareup.leakcanary" && requested.name == “leakcanary-android") { useTarget(mapOf("group" to requested.group, "name" to "leakcanary-android-no-op", "version" to requested.version)) }A }B }C }D
  80. // Kotlin // Ensure the no-op leakcanary dependency is always

    used in JVM tests. configurations.all { if (name.contains("UnitTest")) { resolutionStrategy.eachDependency { if (requested.group == "com.squareup.leakcanary" && requested.name == “leakcanary-android") { useTarget(mapOf("group" to requested.group, "name" to "leakcanary-android-no-op", "version" to requested.version)) }A }B }C }D
  81. // Groovy // Ensure the no-op leakcanary dependency is always

    used in JVM tests. configurations.all { config -> if (config.name.contains("UnitTest")) { config.resolutionStrategy.eachDependency { details -> if (details.requested.group == "com.squareup.leakcanary" && details.requested.name == "leakcanary-android") { details.useTarget(group: details.requested.group, name: "leakcanary-android-no-op", version: details.requested.version) }A }B }C }D