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

Managing Multi-module Android Project with Gradle Plugin and Kotlin

Managing Multi-module Android Project with Gradle Plugin and Kotlin

In this talk, I will discuss how the Gradle plugin works, and how we can write a custom plugin in Kotlin to help us manage an Android multi-module project. I will also present techniques to provide custom configurations in our Gradle plugin so that we can modify our build depending on the module, and further, extend the plugin's capabilities by integrating it with other Gradle plugins; e.g., Jacoco plugin. Lastly, I will share our experience of building a Gradle plugin and explore the benefits and limitations that I've encountered.

6338c8fa4e2e6325094fe30b1e9f9443?s=128

Malvin Sutanto

December 15, 2020
Tweet

Transcript

  1. ©2020 Wantedly, Inc. Managing Multi-module Project with Gradle Plugin and

    Kotlin .droidcon APAC 2020 15 Dec 2020 - Malvin Sutanto Photo by Jan Antonin Kolar on Unsplash
  2. ©2020 Wantedly, Inc. Introduction Malvin Sutanto Software Engineer - Android

    Twitter/ Medium: @malvinsutanto
  3. ©2020 Wantedly, Inc. Multi module setup in Android projects

  4. ©2020 Wantedly, Inc. BQQ MJCOBWJHBUJPO QSFGFSFODFT MJCBOBMZUJDT EBUB OFUXPSL GFBUVSFTFBSDI

    GFBUVSFVTFS Multi module setup in Android
  5. ©2020 Wantedly, Inc. BQQ MJCOBWJHBUJPO QSFGFSFODFT MJCBOBMZUJDT EBUB OFUXPSL GFBUVSFTFBSDI

    GFBUVSFVTFS Multi module setup in Android
  6. ©2020 Wantedly, Inc. Multi module setup in Android

  7. ©2020 Wantedly, Inc. Multi module setup in Android • Faster

    build time
  8. ©2020 Wantedly, Inc. Multi module setup in Android • Faster

    build time • Easier to manage and work with
  9. ©2020 Wantedly, Inc. Multi module setup in Android • Faster

    build time • Easier to manage and work with • New features • Instant apps • Dynamic feature modules
  10. ©2020 Wantedly, Inc. Multi module setup in Android

  11. ©2020 Wantedly, Inc. However Multi module setup in Android

  12. ©2020 Wantedly, Inc. Multi module setup in Android

  13. ©2020 Wantedly, Inc. • Each module need a build.gradle file

    Multi module setup in Android
  14. ©2020 Wantedly, Inc. • Each module need a build.gradle file

    • Duplicate build script across modules Multi module setup in Android
  15. ©2020 Wantedly, Inc. • Each module need a build.gradle file

    • Duplicate build script across modules • Build files became hard to maintain Multi module setup in Android
  16. ©2020 Wantedly, Inc. build.gradle file for a library module Multi

    module setup in Android plugins { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' }
  17. ©2020 Wantedly, Inc. build.gradle file for a library module Multi

    module setup in Android plugins { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } • Declares Gradle plugins • Sets build parameters • Declares dependencies
  18. ©2020 Wantedly, Inc. Gradle plugin Simplifies build scripts

  19. ©2020 Wantedly, Inc. What is Gradle?

  20. ©2020 Wantedly, Inc. What is Gradle?

  21. ©2020 Wantedly, Inc. What is Gradle? • “Generic” build automation

    tool
  22. ©2020 Wantedly, Inc. What is Gradle? • “Generic” build automation

    tool • Declares input and output files
  23. ©2020 Wantedly, Inc. What is Gradle? • “Generic” build automation

    tool • Declares input and output files • Tasks as its core
  24. ©2020 Wantedly, Inc. Gradle Build phases Gradle build phases

  25. ©2020 Wantedly, Inc. Gradle Build phases Gradle build phases *OJUJBMJ[BUJPO

  26. ©2020 Wantedly, Inc. Gradle Build phases Gradle build phases *OJUJBMJ[BUJPO

    $POpHVSBUJPO
  27. ©2020 Wantedly, Inc. Gradle Build phases Gradle build phases *OJUJBMJ[BUJPO

    $POpHVSBUJPO &YFDVUJPO
  28. ©2020 Wantedly, Inc. build.gradle file Domain Specific Language (DSL) plugins

    { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } • Configuration file • Describes build steps • Available in Groovy and Kotlin Gradle Build.gradle file
  29. ©2020 Wantedly, Inc. build.gradle file Domain Specific Language (DSL) plugins

    { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } • Configuration file • Describes build steps • Available in Groovy and Kotlin Extension block Gradle Build.gradle file
  30. ©2020 Wantedly, Inc. build.gradle file Domain Specific Language (DSL) plugins

    { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } • Configuration file • Describes build steps • Available in Groovy and Kotlin Gradle plugins Extension block Gradle Build.gradle file
  31. ©2020 Wantedly, Inc. Gradle plugin

  32. ©2020 Wantedly, Inc. Gradle Plugin

  33. ©2020 Wantedly, Inc. Gradle Plugin • Extends the Gradle model

    • Adds new configuration block and options
  34. ©2020 Wantedly, Inc. Gradle Plugin • Extends the Gradle model

    • Adds new configuration block and options • Configures project according conventions • Adds new tasks • Configures sensible defaults
  35. ©2020 Wantedly, Inc. Gradle Plugin • Extends the Gradle model

    • Adds new configuration block and options • Configures project according conventions • Adds new tasks • Configures sensible defaults • Applies specific configuration • Enforces standards
  36. ©2020 Wantedly, Inc. Types of Gradle Plugin Gradle Plugin

  37. ©2020 Wantedly, Inc. Script plugin • Additional build script that

    is located separately. • Can exist in local filesystem or remote location. • Multiple script plugins can be applied. • apply from: 'other.gradle' Types of Gradle Plugin Gradle Plugin
  38. ©2020 Wantedly, Inc. Script plugin • Additional build script that

    is located separately. • Can exist in local filesystem or remote location. • Multiple script plugins can be applied. • apply from: 'other.gradle' Binary plugin • Written programmatically by implementing Plugin interface, • Applied using unique plugin id • plugins { id 'com.android.library' id 'kotlin-android' } Types of Gradle Plugin Gradle Plugin
  39. ©2020 Wantedly, Inc. Script plugin • Additional build script that

    is located separately. • Can exist in local filesystem or remote location. • Multiple script plugins can be applied. • apply from: 'other.gradle' Binary plugin • Written programmatically by implementing Plugin interface, • Applied using unique plugin id • plugins { id 'com.android.library' id 'kotlin-android' } Types of Gradle Plugin Gradle Plugin
  40. ©2020 Wantedly, Inc. Creating a Gradle Plugin

  41. ©2020 Wantedly, Inc. Gradle Plugin

  42. ©2020 Wantedly, Inc. What will the plugin do? Gradle Plugin

  43. ©2020 Wantedly, Inc. What will the plugin do? For each

    module, it will: Gradle Plugin
  44. ©2020 Wantedly, Inc. What will the plugin do? Gradle Plugin

  45. ©2020 Wantedly, Inc. • Apply other Gradle plugins What will

    the plugin do? Gradle Plugin
  46. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters What will the plugin do? Gradle Plugin
  47. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files What will the plugin do? Gradle Plugin
  48. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features What will the plugin do? Gradle Plugin
  49. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies What will the plugin do? Gradle Plugin
  50. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  51. ©2020 Wantedly, Inc. my-lib/build.gradle.kts Gradle Plugin

  52. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName("debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  53. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  54. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  55. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  56. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  57. ©2020 Wantedly, Inc. plugins { id("com.android.library") id("my-custom-plugin-id") } android {

    buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  58. ©2020 Wantedly, Inc. plugins { id("com.android.library") id("my-custom-plugin-id") } android {

    buildTypes { getByName(“debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } plugins { id 'com.android.library' id 'kotlin-android' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } my-lib/build.gradle.kts Gradle Plugin
  59. ©2020 Wantedly, Inc. Gradle plugin Setup

  60. ©2020 Wantedly, Inc. my-project/ - buildSrc/ - build.gradle.kts - src/main/kotlin/com/mypackage/

    - MyCustomPlugin.kt - app/ - my-lib/ Project Structure Gradle Plugin
  61. ©2020 Wantedly, Inc. my-project/ - buildSrc/ - build.gradle.kts - src/main/kotlin/com/mypackage

    - MyCustomPlugin.kt - app/ - my-lib/ Project Structure Gradle Plugin
  62. ©2020 Wantedly, Inc. buildSrc/src/build.gradle.kts plugins { `java-gradle-plugin` `kotlin-dsl` } repositories

    { google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } Gradle Plugin
  63. ©2020 Wantedly, Inc. plugins { `java-gradle-plugin` `kotlin-dsl` } repositories {

    google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } buildSrc/src/build.gradle.kts Gradle Plugin
  64. ©2020 Wantedly, Inc. plugins { `java-gradle-plugin` `kotlin-dsl` } repositories {

    google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } buildSrc/src/build.gradle.kts Gradle Plugin
  65. ©2020 Wantedly, Inc. plugins { `java-gradle-plugin` `kotlin-dsl` } repositories {

    google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } buildSrc/src/build.gradle.kts Gradle Plugin
  66. ©2020 Wantedly, Inc. plugins { `java-gradle-plugin` `kotlin-dsl` } repositories {

    google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } buildSrc/src/build.gradle.kts Gradle Plugin
  67. ©2020 Wantedly, Inc. plugins { `java-gradle-plugin` `kotlin-dsl` } repositories {

    google() mavenCentral() } dependencies { implementation("com.android.tools.build:gradle:$agpVersion") implementation(kotlin("gradle-plugin")) } https://android-developers.googleblog.com/2020/12/announcing-android-studio-arctic-fox.html buildSrc/src/build.gradle.kts Gradle Plugin
  68. ©2020 Wantedly, Inc. my-project/ - buildSrc/ - build.gradle.kts - src/main/kotlin/com/mypackage/

    - MyCustomPlugin.kt - app/ - my-lib/ Project Structure Gradle Plugin
  69. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  70. ©2020 Wantedly, Inc. Gradle Plugin class MyCustomPlugin : Plugin<Project> {

    buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  71. ©2020 Wantedly, Inc. Gradle Plugin class MyCustomPlugin : Plugin<Project> {

    Module buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  72. ©2020 Wantedly, Inc. Gradle Plugin class MyCustomPlugin : Plugin<Project> {

    override fun apply(project: Project) { // TODO: Write build configurations here. } } Module buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  73. ©2020 Wantedly, Inc. Gradle plugin Implementation

  74. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  75. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  76. ©2020 Wantedly, Inc. override fun apply(project: Project) { Gradle Plugin

    buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  77. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.plugins.apply("kotlin-android") }

    Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  78. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.plugins.apply("kotlin-android") }

    Plugin id // build.gradle plugins { id 'com.android.library' id 'kotlin-android' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  79. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  80. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  81. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  82. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  83. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } // build.gradle android { compileSdkVersion 30 defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  84. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } // build.gradle android { compileSdkVersion 30 defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  85. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } // build.gradle android { compileSdkVersion 30 defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" } } Base class for all Android plugin extension classes Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  86. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } // build.gradle android { compileSdkVersion 30 defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  87. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() } } private fun BaseExtension.applyAndroidSettings() { compileSdkVersion(30) defaultConfig { targetSdkVersion(30) minSdkVersion(23) // etc. } } // build.gradle android { compileSdkVersion 30 defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  88. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  89. ©2020 Wantedly, Inc. Types of plugin Gradle Plugin

  90. ©2020 Wantedly, Inc. Application module Library module id("com.android.application") id("com.android.library") Types

    of plugin Gradle Plugin
  91. ©2020 Wantedly, Inc. Application module • Set isMinifyEnabled only on

    release Library module id("com.android.application") id("com.android.library") Types of plugin Gradle Plugin
  92. ©2020 Wantedly, Inc. Application module • Set isMinifyEnabled only on

    release • Specify proguard files using proguardFiles() Library module id("com.android.application") id("com.android.library") Types of plugin Gradle Plugin
  93. ©2020 Wantedly, Inc. Application module • Set isMinifyEnabled only on

    release • Specify proguard files using proguardFiles() Library module • Specify proguard files using consumerProguardFiles() id("com.android.application") id("com.android.library") Types of plugin Gradle Plugin
  94. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  95. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName(“android") if (androidExtension is BaseExtension) { androidExtension.applyAndroidSettings() androidExtension.applyProguardSettings() } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  96. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  97. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  98. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } proguard-rules.pro files in each module’s root Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  99. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  100. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } // build.gradle plugins { id 'com.android.library' id 'my-custom-plugin-id' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  101. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } // build.gradle plugins { id 'com.android.library' id 'my-custom-plugin-id' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  102. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } // build.gradle plugins { id 'com.android.library' id 'my-custom-plugin-id' } // build.gradle android { defaultConfig { ... consumerProguardFiles "proguard-rules.pro" } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  103. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  104. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } // build.gradle plugins { id 'com.android.application' id 'my-custom-plugin-id' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  105. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Enable obfuscation only on "release" build // build.gradle plugins { id 'com.android.application' id 'my-custom-plugin-id' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  106. ©2020 Wantedly, Inc. private fun BaseExtension.applyProguardSettings() { val proguardFile =

    "proguard-rules.pro" when (this) { is LibraryExtension -> defaultConfig { consumerProguardFiles(proguardFile) } is AppExtension -> buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), proguardFile ) } } } } Enable obfuscation only on "release" build // build.gradle plugins { id 'com.android.application' id 'my-custom-plugin-id' } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  107. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  108. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  109. ©2020 Wantedly, Inc. override fun apply(project: Project) { … val

    androidExtension = project.extensions.getByName("android") if (androidExtension is BaseExtension) { ... androidExtension.enableJava8(project) } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  110. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  111. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  112. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  113. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  114. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  115. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  116. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.tasks.withType<KotlinCompile>().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Java 8 core library desugaring Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  117. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  118. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.dependencies { add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:1.0.9") } ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  119. ©2020 Wantedly, Inc. private fun BaseExtension.enableJava8(project: Project) { compileOptions {

    isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } project.dependencies { add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:1.0.9") } ... } Configuration name Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  120. ©2020 Wantedly, Inc. project.dependencies { add("implementation", "androidx.core:core-ktx:1.2.0") add("testImplementation", "junit:junit:4.12") add("androidTestImplementation",

    "androidx.test.ext:junit:1.1.1") } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  121. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

    common build parameters • Specify default proguard files • Enable Java 8 features • Declare dependencies • Configure Jacoco and custom extensions What will the plugin do? Gradle Plugin
  122. ©2020 Wantedly, Inc. my-lib/build.gradle.kts Gradle Plugin

  123. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName("debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  124. ©2020 Wantedly, Inc. plugins { id("com.android.library") // or .application id("my-custom-plugin-id")

    } android { buildTypes { getByName("debug") { isTestCoverageEnabled = true } } } myPluginOptions { jacoco { isEnabled = true } } dependencies { … } my-lib/build.gradle.kts Gradle Plugin
  125. ©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

  126. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create<MyPluginOptionExtension>("myPluginOptions") …

    if (androidExtension is BaseExtension) { … androidExtension.configureJacoco(project) } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  127. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create<MyPluginOptionExtension>("myPluginOptions") …

    if (androidExtension is BaseExtension) { … androidExtension.configureJacoco(project) } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  128. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create<MyPluginOptionExtension>("myPluginOptions") …

    if (androidExtension is BaseExtension) { … androidExtension.configureJacoco(project) } } Extension class Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  129. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create<MyPluginOptionExtension>("myPluginOptions") …

    if (androidExtension is BaseExtension) { … androidExtension.configureJacoco(project) } } Extension class // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  130. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  131. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  132. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  133. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  134. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  135. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  136. ©2020 Wantedly, Inc. // build.gradle.kts myPluginOptions { jacoco { isEnabled

    = true } } open class MyPluginOptionExtension { val jacoco: JacocoOptions = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } // build.gradle myPluginOptions { jacoco { enabled true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  137. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  138. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  139. ©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions

    = JacocoOptions() fun jacoco(action: Action<JacocoOptions>) { action.execute(jacoco) } } class JacocoOptions { var isEnabled: Boolean = true } // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } https://docs.gradle.org/current/userguide/custom_gradle_types.html https://docs.gradle.org/current/userguide/lazy_configuration.html#lazy_configuration Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyPluginOptionExtension.kt
  140. ©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create<MyPluginOptionExtension>("myPluginOptions") …

    if (androidExtension is BaseExtension) { … androidExtension.configureJacoco(project) } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  141. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  142. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  143. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  144. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  145. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  146. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  147. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  148. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  149. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } "debug" "release" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  150. ©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate {

    val jacocoOptions = project.extensions.getByType<MyPluginOptionExtension>() .jacoco if (jacocoOptions.isEnabled) { project.plugins.apply("jacoco") when (this@configureJacoco) { is LibraryExtension -> configureJacocoTasks(project, libraryVariants) is AppExtension -> configureJacocoTasks(project, applicationVariants) } } } } private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out BaseVariant> ) { ... } "debug" "release" "debug" "release" "flavorDebug" "flavorRelease" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  151. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  152. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  153. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  154. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Skip non-debuggable builds Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  155. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  156. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  157. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } "debug" "release" "flavorDebug" "flavorRelease" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  158. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } "debug" "release" "flavorDebug" "flavorRelease" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  159. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } ./gradlew :lib:jacocoDebugReport ./gradlew :app:jacocoFlavorDebugReport "debug" "release" "flavorDebug" "flavorRelease" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  160. ©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet<out

    BaseVariant> ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register<JacocoReport>("jacoco${variantName.capitalize()}Report") { // Set up the rest of the Jacoco tasks dependency, exec files, etc. } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  161. ©2020 Wantedly, Inc. Apply Gradle plugin on other modules

  162. ©2020 Wantedly, Inc. plugins { ... } gradlePlugin { plugins

    { register("my-custom-plugin") { id = "my-custom-plugin-id" implementationClass = "com.mypackage.MyCustomPlugin" } } } repositories { ... } dependencies { ... } buildSrc/src/build.gradle.kts Gradle Plugin
  163. ©2020 Wantedly, Inc. plugins { ... } gradlePlugin { plugins

    { register("my-custom-plugin") { id = "my-custom-plugin-id" implementationClass = "com.mypackage.MyCustomPlugin" } } } repositories { ... } dependencies { ... } buildSrc/src/build.gradle.kts Gradle Plugin
  164. ©2020 Wantedly, Inc. plugins { ... } gradlePlugin { plugins

    { register("my-custom-plugin") { id = "my-custom-plugin-id" implementationClass = "com.mypackage.MyCustomPlugin" } } } repositories { ... } dependencies { ... } buildSrc/src/build.gradle.kts Gradle Plugin
  165. ©2020 Wantedly, Inc. plugins { ... } gradlePlugin { plugins

    { register("my-custom-plugin") { id = "my-custom-plugin-id" implementationClass = "com.mypackage.MyCustomPlugin" } } } repositories { ... } dependencies { ... } // lib/build.gradle.kts plugins { id("com.android.library") id("my-custom-plugin-id") } buildSrc/src/build.gradle.kts Gradle Plugin
  166. ©2020 Wantedly, Inc. plugins { ... } gradlePlugin { plugins

    { register("my-custom-plugin") { id = "my-custom-plugin-id" implementationClass = "com.mypackage.MyCustomPlugin" } } } repositories { ... } dependencies { ... } // lib/build.gradle.kts plugins { id("com.android.library") id("my-custom-plugin-id") } class MyCustomPlugin : Plugin<Project> { ... } buildSrc/src/build.gradle.kts Gradle Plugin
  167. ©2020 Wantedly, Inc. Gradle plugin Benefits

  168. ©2020 Wantedly, Inc. Benefits Gradle Plugin

  169. ©2020 Wantedly, Inc. Benefits • Share build logic across modules

    • Reduce maintenance cost when updating build scripts Gradle Plugin
  170. ©2020 Wantedly, Inc. Benefits • Share build logic across modules

    • Reduce maintenance cost when updating build scripts • Higher degree of modularization • Better organization and comprehensibility Gradle Plugin
  171. ©2020 Wantedly, Inc. Benefits • Share build logic across modules

    • Reduce maintenance cost when updating build scripts • Higher degree of modularization • Better organization and comprehensibility • Encapsulates logic • Declarative build scripts • Automatically configures tasks and its dependencies Gradle Plugin
  172. ©2020 Wantedly, Inc. Our experience Gradle Plugin

  173. ©2020 Wantedly, Inc. • Helped migration to build.gradle.kts Our experience

    Gradle Plugin
  174. ©2020 Wantedly, Inc. • Helped migration to build.gradle.kts • Make

    use of Gradle API • Apply Gradle plugin to build.gradle files written in Groovy Our experience Gradle Plugin
  175. ©2020 Wantedly, Inc. • Helped migration to build.gradle.kts • Make

    use of Gradle API • Apply Gradle plugin to build.gradle files written in Groovy • Gradually migrate to Kotlin build.gradle.kts files Our experience Gradle Plugin
  176. ©2020 Wantedly, Inc. plugins { id 'com.android.library' id 'kotlin-android' }

    android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } Our experience Gradle Plugin
  177. ©2020 Wantedly, Inc. plugins { id 'com.android.library' id 'kotlin-android' }

    android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } // build.gradle plugin { id 'com.android.library' id 'my-custom-plugin-id' } myPluginOptions { jacoco { enabled true } } Our experience Gradle Plugin
  178. ©2020 Wantedly, Inc. // build.gradle plugin { id 'com.android.library' id

    'my-custom-plugin-id' } myPluginOptions { jacoco { enabled true } } Our experience Gradle Plugin
  179. ©2020 Wantedly, Inc. // build.gradle plugin { id 'com.android.library' id

    'my-custom-plugin-id' } myPluginOptions { jacoco { enabled true } } // build.gradle.kts plugin { id("com.android.library") id("my-custom-plugin-id") } myPluginOptions { jacoco { isEnabled = true } } Our experience Gradle Plugin
  180. ©2020 Wantedly, Inc. Our experience Gradle Plugin

  181. ©2020 Wantedly, Inc. • buildSrc vs standalone plugin Our experience

    Gradle Plugin
  182. ©2020 Wantedly, Inc. • buildSrc vs standalone plugin • Plugin

    is very specific to each project Our experience Gradle Plugin
  183. ©2020 Wantedly, Inc. • buildSrc vs standalone plugin • Plugin

    is very specific to each project • But, split the plugins when necessary depending on its purpose • e.g., Jacoco as its own plugin Our experience Gradle Plugin
  184. ©2020 Wantedly, Inc. Summary

  185. ©2020 Wantedly, Inc. Summary Gradle Plugin

  186. ©2020 Wantedly, Inc. Summary • Maintaining build.gradle files can be

    tedious Gradle Plugin
  187. ©2020 Wantedly, Inc. Summary • Maintaining build.gradle files can be

    tedious • Gradle plugin can simplify the maintenance process Gradle Plugin
  188. ©2020 Wantedly, Inc. Summary • Maintaining build.gradle files can be

    tedious • Gradle plugin can simplify the maintenance process • Make Gradle plugin configurable using extension Gradle Plugin
  189. ©2020 Wantedly, Inc. Thank you @MalvinSutanto