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

Kotlinify your Gradle

Kotlinify your Gradle

Presentation given at Android Makers Paris 2018 https://androidmakers.fr/schedule/#session-45

B11fe994ffca4aaa3da014530981ece3?s=128

Alexander Gherschon

April 23, 2018
Tweet

More Decks by Alexander Gherschon

Other Decks in Programming

Transcript

  1. KOTLINIFY YOUR GRADLE Android Makers Paris 2018 Alexander Gherschon

  2. WHO AM I? • Android Developer @ Houzz • Organiser

    and Speaker @ KotlinTLV.co.il • Love to build tools!
  3. AGENDA • Gradle Plugins • Gradle Kotlin-DSL

  4. GRADLE PLUGINS

  5. WHAT IS GRADLE • Build system (JVM based) • Defines

    Tasks inside a Project • A Plugin is set of Tasks gathered for a purpose • Java, Kotlin, Android, iOS, .Net…
  6. INTERESTING EXISTING PLUGINS • Android Gradle Plugin • Kotlin Gradle

    Plugin • Gradle Android Ribbonizer plugin • Gradle Play Publisher • etc…
  7. BUILDING A GRADLE PLUGIN • Print all “// TODO” lines

    in our project • Find all source files • Find all lines starting with “// TODO” • Android projects (for each build variant)
  8. SOME CONTEXT ANDROID-TODOS-GRADLE-PLUGIN https://github.com/galex/android-todos-gradle-plugin ANDROID-TODOS-TEST-APP https://github.com/galex/android-todos-test-app

  9. USING GRADLE PLUGINS buildscript { ext.kotlin_version = '1.2.21' repositories {

    google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath “org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version” } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android'
  10. buildscript { ext.kv = '1.2.21' repositories { google() jcenter() }

    dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kv" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' USING GRADLE PLUGINS
  11. buildscript { ext.kv = '1.2.21' repositories { google() jcenter() }

    dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kv" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' USING GRADLE PLUGINS
  12. buildscript { ext.kv = '1.2.21' repositories { google() jcenter() }

    dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kv" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' USING GRADLE PLUGINS
  13. WHERE DO THEY LIVE Repo mavenCentral() jcenter() google() Repo mavenLocal()

    buildSrc
  14. GRADLE API INTERFACE<PROJECT> •getDependencies() •getTasks() •… INTERFACE<TASK> •doFirst() •doLast() •…

    INTERFACE<REPOSITORY> •mavenCentral() •…
  15. CREATING A GRADLE PLUGIN

  16. UPDATING GRADLE distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https://services.gradle.org/distributions/gradle-4.6-all.zip ./gradle/wrapper/gradle-wrapper.properties

  17. ADDING DEPENDENCIES ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  18. ADDING DEPENDENCIES ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  19. ADDING DEPENDENCIES ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  20. ADDING DEPENDENCIES ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  21. NAMING OUR PLUGIN ./build.gradle ext { pluginGroupId = 'il.co.galex.tools.build' pluginArtifactId

    = 'todos' pluginVersion = '0.0.1' }
  22. DEPLOYING LOCALLY ./build.gradle apply plugin: 'maven-publish' publishing { publications {

    TodosPublication(MavenPublication) { groupId = pluginGroupId artifactId = pluginArtifactId version = pluginVersion from components.java } } }
  23. DEPLOYING LOCALLY - PUBLISH TASK

  24. DEPENDING & APPLYING ./build.gradle buildscript { repositories { mavenLocal() }

    dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "il.co.galex.tools.build:todos:0.0.1" } } apply plugin: 'il.co.galex.todos' ./app/build.gradle
  25. DEPENDING & APPLYING ./build.gradle buildscript { repositories { mavenLocal() }

    dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "il.co.galex.tools.build:todos:0.0.1" } } apply plugin: 'il.co.galex.todos' ./app/build.gradle
  26. APPLYING OUR PLUGIN ./app/build.gradle apply plugin: 'il.co.galex.todos'

  27. GRADLE PLUGIN ENTRY POINT apply plugin: 'il.co.galex.todos' ./src/main/resources/ il.co.galex.todos.properties META-INF/gradle-plugins/

    MATCH!
  28. GRADLE PLUGIN ENTRY POINT ./src/…/META-INF/gradle-plugins/il.co.galex.todos.properties implementation-class=il.co.galex.tools.todos.plugin.TodosPlugin /src/main/kotlin/…/il.co.galex.tools.todos.plugin.TodosPlugin class TodosPlugin: Plugin<Project>

    { override fun apply(project: Project) { TODO("We will implement here our plugin!") } }
  29. RESULT!

  30. IMPLEMENTING OUR PLUGIN class TodosPlugin: Plugin<Project> { override fun apply(project:

    Project?) { TODO("We will implement here our plugin!") } }
  31. IMPLEMENTING OUR PLUGIN class TodosPlugin: Plugin<Project> { override fun apply(project:

    Project) { } }
  32. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val hasApp

    = project.plugins.hasPlugin(AppPlugin::class.java) val hasLib = project.plugins.hasPlugin(LibraryPlugin::class.java) if (!hasApp && !hasLib) { throw IllegalStateException(“'some warning here”) } (…) }
  33. DEPENDENCIES (REMINDER) ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  34. DEPENDENCIES (REMINDER) ./build.gradle repositories { jcenter() google() } dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.12' compile gradleApi() compileOnly 'com.android.tools.build:gradle:3.0.1' }
  35. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val hasApp

    = project.plugins.hasPlugin(AppPlugin::class.java) val hasLib = project.plugins.hasPlugin(LibraryPlugin::class.java) if (!hasApp && !hasLib) { throw IllegalStateException(“'some warning here”) } (…) }
  36. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val hasApp

    = project.plugins.hasPlugin(AppPlugin::class.java) val hasLib = project.plugins.hasPlugin(LibraryPlugin::class.java) if (!hasApp && !hasLib) { throw IllegalStateException(“'some warning here”) } (…) }
  37. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val hasApp

    = project.plugins.hasPlugin(AppPlugin::class.java) val hasLib = project.plugins.hasPlugin(LibraryPlugin::class.java) if (!hasApp && !hasLib) { throw IllegalStateException(“'some warning here”) } (…) }
  38. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { (…) val

    variants: DomainObjectSet<out BaseVariant> = if (hasApp) { project.extensions.findByType(AppExtension::class.java)!!.applicationVariants } else { project.extensions.findByType(LibraryExtension::class.java)!!.libraryVariants } (…) }
  39. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { (…) val

    variants: DomainObjectSet<out BaseVariant> = if (hasApp) { project.extensions.findByType(AppExtension::class.java)!!.applicationVariants } else { project.extensions.findByType(LibraryExtension::class.java)!!.libraryVariants } (…) }
  40. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val variants:

    DomainObjectSet<out BaseVariant> = (…) variants.all { variant: BaseVariant -> variant.sourceSets.flatMap { it.javaDirectories } .forEach { println(it) } } }
  41. IMPLEMENTING OUR PLUGIN override fun apply(project: Project) { val variants:

    DomainObjectSet<out BaseVariant> = (…) variants.all { variant: BaseVariant -> variant.sourceSets.flatMap { it.javaDirectories } .forEach { println(it) } } }
  42. CURRENT RESULT

  43. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  44. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  45. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  46. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  47. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  48. CURRENT RESULT

  49. 3 PHASES OF GRADLE INITIALIZATION CONFIGURATION EXECUTION

  50. 3 PHASES OF GRADLE INITIALIZATION CONFIGURATION EXECUTION

  51. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  52. IMPLEMENTING OUR PLUGIN variants.all { variant: BaseVariant -> val folders

    = variant.sourceSets.flatMap { it.javaDirectories } val taskName = "print${variant.name.capitalize()}Todos" val task = project.tasks.create(taskName, TodosTask::class.java) { it.folders = folders } task.group = "Todos" task.description = "Find all todos in the project for variant ${variant.name}" }
  53. THE TODOS TASK open class TodosTask : DefaultTask() { var

    folders: List<File>? = null }
  54. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  55. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  56. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  57. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  58. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  59. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  60. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  61. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  62. THE TODOS TASK open class TodosTask : DefaultTask() { @TaskAction

    fun run() { folders?.forEach { folder -> folder.walk().filter { it.isFile }.forEach { file -> file.readLines().forEachIndexed { index, line -> if (line.contains("// TODO")) { println("$file:${index + 1} - ${line.trim()}") } } } } } }
  63. CURRENT RESULT

  64. RUNNING TASK - VARIANT DEBUG

  65. RUNNING TASK - VARIANT RELEASE

  66. WHAT ABOUT PARAMETERS?

  67. USING AN EXTENSION ./build.gradle todos { keywords = ['TODO', 'WTH']

    }
  68. WRITING OUR EXTENSION open class TodosExtension { var keywords: List<String>?

    = null set(value) { when { value == null -> throw IllegalArgumentException("...") value.isEmpty() -> throw IllegalArgumentException("...") value.find { it == "" } != null -> throw IllegalArgumentException("...") else -> field = value } } }
  69. WRITING OUR EXTENSION open class TodosExtension { var keywords: List<String>?

    = null set(value) { when { value == null -> throw IllegalArgumentException("...") value.isEmpty() -> throw IllegalArgumentException("...") value.find { it == "" } != null -> throw IllegalArgumentException("...") else -> field = value } } }
  70. WRITING OUR EXTENSION open class TodosExtension { var keywords: List<String>?

    = null set(value) { when { value == null -> throw IllegalArgumentException("...") value.isEmpty() -> throw IllegalArgumentException("...") value.find { it == "" } != null -> throw IllegalArgumentException("...") else -> field = value } } }
  71. USING OUR EXTENSION IN OUR PLUGIN class TodosPlugin : Plugin<Project>

    { override fun apply(project: Project) { project.extensions.create("todos", TodosExtension::class.java) } }
  72. USING OUR EXTENSION IN OUR PLUGIN class TodosPlugin : Plugin<Project>

    { override fun apply(project: Project) { project.extensions.create("todos", TodosExtension::class.java) } }
  73. USING OUR EXTENSION IN OUR TASK val extension: TodosExtension? =

    project.extensions.findByType(TodosExtension::class.java) val keywords = extension?.keywords ?: listOf(“TODO") file.readLines().forEachIndexed { index, line -> if (line.trim().startsWith("//") && keywords.intersect(line.split(" ")).isNotEmpty()) { println("$file:${index + 1} - ${line.trim()}") } }
  74. USING OUR EXTENSION IN OUR TASK val extension: TodosExtension? =

    project.extensions.findByType(TodosExtension::class.java) val keywords = extension?.keywords ?: listOf(“TODO") file.readLines().forEachIndexed { index, line -> if (line.trim().startsWith("//") && keywords.intersect(line.split(" ")).isNotEmpty()) { println("$file:${index + 1} - ${line.trim()}") } }
  75. USING OUR EXTENSION IN OUR TASK val extension: TodosExtension? =

    project.extensions.findByType(TodosExtension::class.java) val keywords = extension?.keywords ?: listOf(“TODO") file.readLines().forEachIndexed { index, line -> if (line.trim().startsWith("//") && keywords.intersect(line.split(" ")).isNotEmpty()) { println("$file:${index + 1} - ${line.trim()}") } }
  76. DEFINING OUR EXTENSION ./build.gradle todos { keywords = ['TODO', 'WTF']

    }
  77. RUNNING TASK - VARIANT DEBUG

  78. • Built a Gradle Plugin • Created a task per

    Android Build Variant • Used an extension for as plugin parameters RECAP
  79. GRADLE KOTLIN-DSL

  80. WHAT IS A DSL • Domain Specific Language • General

    purpose Programming Language
  81. SIMPLE DSL EXAMPLE enum class Color { BLUE, RED, GREEN

    } data class Wheels(var number: Int? = null, var color: Color? = null) data class Vehicle(var wheels: Wheels? = null, var engine: String? = null)
  82. SIMPLE DSL EXAMPLE // imperative way val carImperative = Vehicle(Wheels(2,

    Color.RED), "Meh") println(carImperative) // Vehicle(wheels=Wheels(number=2, color=RED), engine=Meh)
  83. SIMPLE DSL EXAMPLE // declarative way val carDSL = vehicle

    { engine = "V8" wheels { number = 4 color = Color.BLUE } }
  84. SIMPLE DSL EXAMPLE private fun vehicle(block: Vehicle.() -> Unit) =

    Vehicle().apply(block) // declarative way val carDSL = vehicle { engine = "V8" wheels { number = 4 color = Color.BLUE } } SIMPLE DSL EXAMPLE
  85. SIMPLE DSL EXAMPLE private fun vehicle(block: Vehicle.() -> Unit) =

    Vehicle().apply(block) // declarative way val carDSL = vehicle { engine = "V8" wheels { number = 4 color = Color.BLUE } } SIMPLE DSL EXAMPLE
  86. SIMPLE DSL EXAMPLE // declarative way val carDSL = vehicle

    { this.engine = "V8" wheels { number = 4 color = Color.BLUE } } private fun vehicle(block: Vehicle.() -> Unit) = Vehicle().apply(block) SIMPLE DSL EXAMPLE
  87. THE APPLY FUNCTION private fun vehicle(block: Vehicle.() -> Unit) =

    Vehicle().apply(block) private fun vehicle(block: Vehicle.() -> Unit): Vehicle { val vehicle = Vehicle() vehicle.block() return vehicle }
  88. APPLY FUNCTION private fun vehicle(block: Vehicle.() -> Unit) = Vehicle().apply(block)

    private fun vehicle(block: Vehicle.() -> Unit): Vehicle { val vehicle = Vehicle() vehicle.block() return vehicle }
  89. SIMPLE DSL EXAMPLE // declarative way val carDSL = vehicle

    { engine = "V8" wheels { number = 4 color = Color.BLUE } }
  90. SIMPLE DSL EXAMPLE // declarative way val carDSL = vehicle

    { this.engine = "V8" this.wheels { this.number = 4 this.color = Color.BLUE } }
  91. SIMPLE DSL EXAMPLE private fun Vehicle.wheels(block: Wheels.() -> Unit) {

    this.wheels = Wheels().apply(block) } // declarative way val carDSL = vehicle { this.engine = "V8" this.wheels { this.number = 4 this.color = Color.BLUE } }
  92. SIMPLE DSL EXAMPLE private fun Vehicle.wheels(block: Wheels.() -> Unit) {

    this.wheels = Wheels().apply(block) } // declarative way val carDSL = vehicle { this.engine = "V8" this.wheels { this.number = 4 this.color = Color.BLUE } }
  93. SIMPLE DSL EXAMPLE private fun Vehicle.wheels(block: Wheels.() -> Unit) {

    this.wheels = Wheels().apply(block) } // declarative way val carDSL = vehicle { this.engine = "V8" this.wheels { this.number = 4 this.color = Color.BLUE } }
  94. SIMPLE DSL EXAMPLE private fun Vehicle.wheels((block: Wheels.() -> Unit) {

    this.wheels = Wheels().apply(block) } // declarative way val carDSL = vehicle { this.engine = "V8" this.wheels { this.number = 4 this.color = Color.BLUE } } Lambda with Receiver = Lambda, with inside “this”
  95. GRADLE API INTERFACE<PROJECT> •getDependencies() •getTasks() •… INTERFACE<TASK> •doFirst() •doLast() •…

    INTERFACE<REPOSITORY> •mavenCentral() •…
  96. WHAT IS THE KOTLIN-DSL • Gradle API • Set of

    Extensions functions and lambdas with receiver • IntelliJ ‘extension’ which understands .kts build files
  97. PS: CONVERT TO K IN TWO GIT ‘PHASES’ 1.Rename file

    then commit 2.Modify its content then commit and push
  98. PS: CONVERT TO K IN TWO GIT ‘PHASES’ 1.build.gradle ->

    build.gradle.kts + commit 2.Modify build.gradle.kts + commit + push
  99. CONVERTING OUR PLUGIN - 1 buildscript { ext.kotlin_version = '1.2.31'

    repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'java' apply plugin: 'kotlin' apply plugin: 'maven-publish'
  100. CONVERTING OUR PLUGIN - 1’ plugins { kotlin("jvm") version "1.2.40"

    `java-gradle-plugin` `maven-publish` }
  101. CONVERTING OUR PLUGIN - 2 compileKotlin { kotlinOptions.jvmTarget = "1.8"

    }
  102. CONVERTING OUR PLUGIN - 2' tasks { withType<KotlinCompile> { kotlinOptions.jvmTarget

    = "1.8" } }
  103. CONVERTING OUR PLUGIN - 3 task sourceJar(type: Jar) { from

    sourceSets.main.allJava } task javadocJar(type: Jar, dependsOn: javadoc) { from javadoc.destinationDir }
  104. CONVERTING OUR PLUGIN - 3’ val sourceJar = task<Jar>("sourceJar") {

    from(java.sourceSets["main"].allSource) classifier = "sources" } val javadocJar: Jar = task<Jar>("javadocJar") { dependsOn("javadoc") from(java.docsDir) classifier = "javadoc" }
  105. CONVERTING OUR PLUGIN - 4 ext { pluginGroupId = 'il.co.galex.tools.build'

    pluginArtifactId = 'todos' pluginVersion = '0.0.2' }
  106. CONVERTING OUR PLUGIN - 4’ ext { set("pluginGroupId", "il.co.galex.tools.build") set("pluginArtifactId",

    "todos") set("pluginVersion", "0.0.5") }
  107. CONVERTING OUR PLUGIN - 5 publishing { publications { TodosPublication(MavenPublication)

    { groupId = pluginGroupId artifactId = pluginArtifactId version = pluginVersion from components.java artifact sourceJar { classifier "sources" } artifact javadocJar { classifier = 'javadoc' } } } }
  108. CONVERTING OUR PLUGIN - 5’ publishing { publications { create<MavenPublication>("TodosPublication")

    { groupId = ext["pluginGroupId"] as String? artifactId = ext["pluginArtifactId"] as String? version = ext["pluginVersion"] as String? from(components["java"]) artifact(sourceJar) artifact(javadocJar) } } }
  109. CONVERTING OUR ANDROID APP - 1 android { compileSdkVersion 26

    defaultConfig { applicationId "il.co.galex.todosgradleplugintest" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } (...) }
  110. CONVERTING OUR ANDROID APP - 1’ android { compileSdkVersion(26) defaultConfig

    { applicationId = "il.co.galex.todosgradleplugintest" minSdkVersion(15) targetSdkVersion(26) versionCode = 1 versionName = "1.0" testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" } (...) }
  111. CONVERTING OUR ANDROID APP - 2 signingConfigs { debug {

    storeFile file("debug.keystore") } } buildTypes { debug { signingConfig signingConfigs.debug minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
  112. CONVERTING OUR ANDROID APP - 2’ signingConfigs { getByName("debug") {

    storeFile = file("debug.keystore") } } buildTypes { getByName("debug") { signingConfig = signingConfigs.getByName("debug") isMinifyEnabled = false proguardFile(getDefaultProguardFile ("proguard-android.txt")) proguardFile("proguard-rules.pro") } }
  113. CONVERTING OUR ANDROID APP - 3 sourceSets { main {

    java.srcDirs += 'src/main/kotlin' } debug { java.srcDirs += 'src/debug/kotlin' } release { java.srcDirs += 'src/release/kotlin' } }
  114. CONVERTING OUR ANDROID APP - 3’ sourceSets { getByName("main") {

    this.java.srcDir("src/main/kotlin") } getByName("debug") { this.java.srcDir("src/debug/kotlin") } getByName("release") { this.java.srcDir("src/release/kotlin") } }
  115. CONVERTING OUR ANDROID APP - 4 dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' }
  116. CONVERTING OUR ANDROID APP - 4’ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") implementation("com.android.support:appcompat-v7:26.1.0")

    implementation("com.android.support.constraint:constraint-layout:1.0.2") testImplementation("junit:junit:4.12") androidTestImplementation("com.android.support.test:runner:1.0.1") androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.1") }
  117. CONVERTING OUR ANDROID APP - 5 todos { keywords =

    [“TODO", “WTH"] }
  118. CONVERTING OUR ANDROID APP - 5 todos { keywords =

    listOf("TODO", "WTH") }
  119. RECAP • Converted from Groovy to Kotlin • Not that

    easy to switch • Probably because it’s quite early… v0.1.6.3 • But it’s Kotlin, it’s fun!
  120. • Todos Gradle Plugin https://github.com/galex/android-todos-gradle-plugin • Android Test App https://github.com/galex/android-todos-test-app

    • Some good blogposts on the Kotlin-DSL • https://antonioleiva.com/kotlin-dsl-gradle/ • https://kotlinexpertise.com/gradlekotlindsl/ • Books • Kotlin in Action https://www.manning.com/books/kotlin-in-action • Gradle in Action https://www.manning.com/books/gradle-in-action LINKS
  121. THANK YOU! Questions? @GALEX