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

Managing Multi-module Android Project with Grad...

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.

Malvin Sutanto

December 15, 2020
Tweet

More Decks by Malvin Sutanto

Other Decks in Programming

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. Multi module setup in Android • Faster

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

    build time • Easier to manage and work with • New features • Instant apps • Dynamic feature modules
  4. ©2020 Wantedly, Inc. • Each module need a build.gradle file

    • Duplicate build script across modules Multi module setup in Android
  5. ©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
  6. ©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' }
  7. ©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
  8. ©2020 Wantedly, Inc. What is Gradle? • “Generic” build automation

    tool • Declares input and output files • Tasks as its core
  9. ©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
  10. ©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
  11. ©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
  12. ©2020 Wantedly, Inc. Gradle Plugin • Extends the Gradle model

    • Adds new configuration block and options
  13. ©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
  14. ©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
  15. ©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
  16. ©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
  17. ©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
  18. ©2020 Wantedly, Inc. • Apply other Gradle plugins • Set

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

    common build parameters • Specify default proguard files What will the plugin do? Gradle Plugin
  20. ©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
  21. ©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
  22. ©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
  23. ©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
  24. ©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
  25. ©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
  26. ©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
  27. ©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
  28. ©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
  29. ©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
  30. ©2020 Wantedly, Inc. my-project/ - buildSrc/ - build.gradle.kts - src/main/kotlin/com/mypackage/

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

    - MyCustomPlugin.kt - app/ - my-lib/ Project Structure Gradle Plugin
  32. ©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
  33. ©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
  34. ©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
  35. ©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
  36. ©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
  37. ©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
  38. ©2020 Wantedly, Inc. my-project/ - buildSrc/ - build.gradle.kts - src/main/kotlin/com/mypackage/

    - MyCustomPlugin.kt - app/ - my-lib/ Project Structure Gradle Plugin
  39. ©2020 Wantedly, Inc. Gradle Plugin class MyCustomPlugin : Plugin<Project> {

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

    Module buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  41. ©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
  42. ©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
  43. ©2020 Wantedly, Inc. override fun apply(project: Project) { Gradle Plugin

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

    Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt
  45. ©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
  46. ©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
  47. ©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
  48. ©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
  49. ©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
  50. ©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
  51. ©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
  52. ©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
  53. ©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
  54. ©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
  55. ©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
  56. ©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
  57. ©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
  58. ©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
  59. ©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
  60. ©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
  61. ©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
  62. ©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
  63. ©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
  64. ©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
  65. ©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
  66. ©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
  67. ©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
  68. ©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
  69. ©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
  70. ©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
  71. ©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
  72. ©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
  73. ©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
  74. ©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
  75. ©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
  76. ©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
  77. ©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
  78. ©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
  79. ©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
  80. ©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
  81. ©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
  82. ©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
  83. ©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
  84. ©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
  85. ©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
  86. ©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
  87. ©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
  88. ©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
  89. ©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
  90. ©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
  91. ©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
  92. ©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
  93. ©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
  94. ©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
  95. ©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
  96. ©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
  97. ©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
  98. ©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
  99. ©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
  100. ©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
  101. ©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
  102. ©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
  103. ©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
  104. ©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
  105. ©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
  106. ©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
  107. ©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
  108. ©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
  109. ©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
  110. ©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
  111. ©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
  112. ©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
  113. ©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
  114. ©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
  115. ©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
  116. ©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
  117. ©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
  118. ©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
  119. ©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
  120. ©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
  121. ©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
  122. ©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
  123. ©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
  124. ©2020 Wantedly, Inc. Benefits • Share build logic across modules

    • Reduce maintenance cost when updating build scripts Gradle Plugin
  125. ©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
  126. ©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
  127. ©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
  128. ©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
  129. ©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
  130. ©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
  131. ©2020 Wantedly, Inc. // build.gradle plugin { id 'com.android.library' id

    'my-custom-plugin-id' } myPluginOptions { jacoco { enabled true } } Our experience Gradle Plugin
  132. ©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
  133. ©2020 Wantedly, Inc. • buildSrc vs standalone plugin • Plugin

    is very specific to each project Our experience Gradle Plugin
  134. ©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
  135. ©2020 Wantedly, Inc. Summary • Maintaining build.gradle files can be

    tedious • Gradle plugin can simplify the maintenance process Gradle Plugin
  136. ©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