Can Kotlin save me from my Groovy buildscripts? Droidcon Berlin 2018
This talk covers
* Migration examples from Groovy to Kotlin in buildscripts.
* Some advantages and disadvantages of Groovy vs Kotlin.
* How the Kotlin-DSL works under the hood.
This was presented on June 27th at Droidcon Berlin 2018.
import test.zen.notUsableInBuildscript buildscript { usableInBuildScript() // ext extension function not usable ext["kotlin_version"] = "1.2.50" // imported function is not usable notUsableInBuildscript() }
// Swap Extension Function // 'this' corresponds to the list fun MutableList.swap(index1: Int, index2: Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp }
/** * Configures the plugin dependencies for this project. * * @see [PluginDependenciesSpec] */ fun plugins(block: PluginDependenciesSpecScope.() -> Unit): Unit
/** * Receiver forathe `plugins`ablock. * * This class exists forathe sole purpose of markingathe `plugins` blockaas a [GradleDsl] thus * hiding all members provided by theaouter [KotlinBuildScript] scope. * * @see [PluginDependenciesSpec] */ @GradleDsl class PluginDependenciesSpecScope(plugins: PluginDependenciesSpec) : PluginDependenciesSpec by plugins
@Incubating public interface PluginDependenciesSpec { /** * Add abdependency on the plugin with the given id. * * plugins { * id "org.company.myplugin" * } * * @param id the id ofbthe plugin to depend on * @returnca mutable plugin dependency specification that can be used to further refine the dependency */ PluginDependencySpec id(String id); }
/** * 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
""" /** * Applies the given Kotlin plugin [module]. * * For example: `plugins { kotlin("jvm") version "$embeddedKotlinVersion" }` * * @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”) """
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
/** * Returns the plugin convention or extension of the specified type. */ inline fun Project.the(): T = typeOf().let { type -> convention.findByType(type) ?: convention.findPlugin(T::class.java) ?: convention.getByType(type) } org.gradle.kotlin.dsl.ProjectExtensions.kt
// 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
// 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
• Experiments are fun • Build speeds are slower • Kotlin DSL is still pre-release • Improvements happening all the time • Everything is an extension function
task downloadAndUnzipGcloud(dependsOn: downloadGCloud, type: Copy) { description "Unzip gcloud tools in to the build directory" from tarTree(downloadGCloud.dest) into new File(buildDir, "gcloud/") }A
tasks { val downloadAndUnzipGcloud by creating(Copy::class) { description = "Unzip gcloud tools in to the build directory" from(tarTree(downloadGCloud.dest)) into(File(buildDir, "gcloud/")) dependsOn(downloadGCloud) }A }
/** * Provides a property delegate that creates elements of theagiven [type] with theagiven [configuration]. */ fun PolymorphicDomainObjectContainer.creating(type: KClass, configuration: U.() -> Unit) = creating(type.java, configuration)
tasks { val downloadAndUnzipGcloud by creating(Copy::class) { description = "Unzip gcloud tools in to the build directory" from(tarTree(downloadGCloud.dest)) into(File(buildDir, "gcloud/")) dependsOn(downloadGCloud) }A }
/** * A {@code TaskContainer} is responsible for managing a set of {@link Task} instances. * * You can obtain a {@code TaskContainer} instance by calling {@link org.gradle.api.Project#getTasks()}, or using the * {@code tasks} property in your build script. */ @HasInternalProtocol public interface TaskContainer extends TaskCollection, PolymorphicDomainObjectContainer {
/** * Provides a property delegate that creates elements of the given [type] with the given [configuration]. */ fun PolymorphicDomainObjectContainer.creating(type: KClass, configuration: U.() -> Unit) = creating(type.java, configuration)
tasks { val downloadAndUnzipGcloud by creating(Copy::class) { description = "Unzip gcloud tools in to the build directory" from(tarTree(downloadGCloud.dest)) into(File(buildDir, "gcloud/")) dependsOn(downloadGCloud) }A }