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

Make Gradle work for you - Create your own Task...

MWM
April 30, 2023

Make Gradle work for you - Create your own Tasks and Plugins

As Android developers, we often interact with build.gradle(.kts) and settings.gradle(.kts) to build our apps with Gradle. Sometimes, we add a plugin that magically provides us with some needed features (aka Tasks) without knowing how it works under the hood.

Discover the power of Gradle and how you can script some manual and painful tasks to make Gradle work for you.

During this presentation, you will learn:

- What is a Task, and how to create your own
- What is a Plugin, and how to create your own
- Provide a nice DSL to configure your tasks
- How to publish your plugin

MWM

April 30, 2023
Tweet

More Decks by MWM

Other Decks in Programming

Transcript

  1. What are we going to talk about ? We usually

    do the same things in our gradle files: - Android configuration - Dependencies - Repository / plugin configurations - … Our main issue as Android developers: - We are usually not comfortable with custom tasks - When they must evolve we don’t really know what we are doing 3
  2. Our goal today - Give you the tools to be

    comfortable with writing your own Gradle Tasks and Plugins - Understand what we do on a daily basis with regular/common plugins 4
  3. What is Gradle - Build automation tool - Open Source

    - Can be used with Kotlin or Groovy 5
  4. What is a Task? “A Task represents some atomic piece

    of work which a build performs” • compiling some classes • creating a JAR • generating Javadoc • publishing some archives to a repository. 6
  5. Where to put the Task code (1/3) In the build.gradle.kts

    of the Gradle module we want the Task. Pros • Directly usable without doing anything else. Cons • Not accessible outside the script. 8
  6. Where to put the Task code (2/3) In the buildSrc

    of the Android Project. Pros • Automatically accessible to all modules of the project. • Task code separated from where it’s used. Cons • A change in the code in buildSrc make the whole project to become out-of-date. 9
  7. Where to put the Task code (3/3) In a standalone

    module. Pros • Possible to release the generated JAR in a repository to use it in another project. Cons • Longer setup • Possibly overkill depending on your needs 10
  8. Let’s configure our “buildSrc” plugins { `kotlin-dsl` } repositories {

    // The org.jetbrains.kotlin.jvm plugin requires a repository // where to download the Kotlin compiler dependencies from. mavenCentral() } 12 buildSrc/build.gradle.kts
  9. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 13 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  10. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 14 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  11. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 15 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  12. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 16 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  13. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 17 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  14. abstract class BundleReleaseFilesTask : DefaultTask() { @get:InputDirectory abstract val projectDir:

    DirectoryProperty @get:Input abstract val appVersion: Property<String> @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun run() { // TODO } } 18 buildSrc/src/main/kotlin/BundleReleaseFilesTask.kt
  15. Incremental build > Task :app:validateSigningDebug UP-TO-DATE > Task :app:writeDebugAppMetadata UP-TO-DATE

    > Task :app:writeDebugSigningConfigVersions UP-TO-DATE > Task :app:packageDebug UP-TO-DATE > Task :app:createDebugApkListingFileRedirect UP-TO-DATE > Task :app:assembleDebug UP-TO-DATE 19
  16. Register the Task in your app module tasks.register<BundleReleaseFilesTask>("bundleReleaseFiles") { projectDir.set(File("."))

    appVersion.set("1.00.00") outputDirectory.set(File("build/outputs")) } 20 app/build.gradle.kt
  17. Cool task ! Let’s reuse it elsewhere But… - We

    don’t want to copy paste the task everywhere - We may depend on other tasks for ours to be working properly 23
  18. Enter plugins ! - A single/multiple tasks can be packaged

    in a plugin - A plugin encapsulates tasks and their dependencies (dependOn, …) 24
  19. class BundleReleaseFilesPlugin : Plugin<Project> { override fun apply(target: Project) {

    // TODO } } 26 buildSrc/src/main/kotlin/BundleReleaseFilesPlugin.kt
  20. class BundleReleaseFilesPlugin : Plugin<Project> { override fun apply(target: Project) {

    val bundleReleaseFilesTask = target.tasks.register( "bundleReleaseFiles", BundleReleaseFilesTask::class.java ) } } 27 buildSrc/src/main/kotlin/BundleReleaseFilesPlugin.kt
  21. class BundleReleaseFilesPlugin : Plugin<Project> { override fun apply(target: Project) {

    val bundleReleaseFilesTask = target.tasks.register( "bundleReleaseFiles", BundleReleaseFilesTask::class.java ) { this.group = "my_plugin" this.description = "Bundle release files (APK, Bundle, mapping) in the same directory" } } } 28 buildSrc/src/main/kotlin/BundleReleaseFilesPlugin.kt
  22. Extension: A DSL for the Plugin’s parameters interface BundleReleaseFilesPluginExtension {

    val appVersion: Property<String> val outputDirectory: DirectoryProperty } 29 buildSrc/src/main/kotlin/BundleReleaseFilesPluginExtension.kt
  23. class BundleReleaseFilesPlugin : Plugin<Project> { override fun apply(target: Project) {

    … val extension = target.extensions.create( "bundleReleaseFiles", BundleReleaseFilesPluginExtension::class.java ) } } 30 buildSrc/src/main/kotlin/BundleReleaseFilesPlugin.kt
  24. class BundleReleaseFilesPlugin : Plugin<Project> { override fun apply(target: Project) {

    val extension = target.extensions.create( "bundleReleaseFiles", BundleReleaseFilesPluginExtension::class.java ) bundleReleaseFilesTask.configure { projectDir.set(target.projectDir) appVersion.set(extension.appVersion) outputDirectory.set(extension.outputDirectory) } } } 31 buildSrc/src/main/kotlin/BundleReleaseFilesPlugin.kt
  25. How to expose the plugin plugins { id("java-gradle-plugin") } gradlePlugin

    { plugins { create("bundleReleaseFiles") { id = "com.mwm.bundle_release_files" implementationClass = "BundleReleaseFilesPlugin" } } } 32 buildSrc/build.gradle.kts
  26. Plugin project structure - Best practice is to have the

    plugin in a dedicated module - This is still a Gradle module: - src/main/kotlin - tests - build.gradle.kts 34
  27. Set the plugin available in the project repository // root

    settings.gradle.kts includeBuild("bundle_release_plugin") 35
  28. Set the plugin available in the project repository // root

    settings.gradle.kts includeBuild("bundle_release_plugin") 36
  29. Prepare for publication plugins { id("maven-publish") } publishing { publications

    { create<MavenPublication>("java") { from(components.getByName("java")) groupId = "com.mwm" artifactId = "bundle_release_files" version = "1.00.00" } } } 37 bundle_release_plugin/build.gradle.kts
  30. Conclusion How to create a Task - extends DefaultTask -

    Property parameters - @get:Input / @get:output How to instantiate a Task in a module - tasks.register How to create a Plugin - implement Plugin<T> - Java-gradle-plugin - Extension - Release a plugin with maven-publish How to apply a Plugin in a module - id(“my.plugin.id”) 39
  31. To go further - Make our Task depend on the

    assembleRelease / bundleRelease Task. - Improve DX by providing default parameter values using Convention. - Improve our DSL to hide direct calls to Property.set 40
  32. 41