Slide 1

Slide 1 text

©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

Slide 2

Slide 2 text

©2020 Wantedly, Inc. Introduction Malvin Sutanto Software Engineer - Android Twitter/ Medium: @malvinsutanto

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

©2020 Wantedly, Inc. Multi module setup in Android

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

©2020 Wantedly, Inc. Multi module setup in Android

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

©2020 Wantedly, Inc. Multi module setup in Android

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

©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

Slide 16

Slide 16 text

©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' }

Slide 17

Slide 17 text

©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

Slide 18

Slide 18 text

©2020 Wantedly, Inc. Gradle plugin Simplifies build scripts

Slide 19

Slide 19 text

©2020 Wantedly, Inc. What is Gradle?

Slide 20

Slide 20 text

©2020 Wantedly, Inc. What is Gradle?

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

©2020 Wantedly, Inc. Gradle Build phases Gradle build phases

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

©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

Slide 29

Slide 29 text

©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

Slide 30

Slide 30 text

©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

Slide 31

Slide 31 text

©2020 Wantedly, Inc. Gradle plugin

Slide 32

Slide 32 text

©2020 Wantedly, Inc. Gradle Plugin

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

©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

Slide 35

Slide 35 text

©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

Slide 36

Slide 36 text

©2020 Wantedly, Inc. Types of Gradle Plugin Gradle Plugin

Slide 37

Slide 37 text

©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

Slide 38

Slide 38 text

©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

Slide 39

Slide 39 text

©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

Slide 40

Slide 40 text

©2020 Wantedly, Inc. Creating a Gradle Plugin

Slide 41

Slide 41 text

©2020 Wantedly, Inc. Gradle Plugin

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

©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

Slide 49

Slide 49 text

©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

Slide 50

Slide 50 text

©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

Slide 51

Slide 51 text

©2020 Wantedly, Inc. my-lib/build.gradle.kts Gradle Plugin

Slide 52

Slide 52 text

©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

Slide 53

Slide 53 text

©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

Slide 54

Slide 54 text

©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

Slide 55

Slide 55 text

©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

Slide 56

Slide 56 text

©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

Slide 57

Slide 57 text

©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

Slide 58

Slide 58 text

©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

Slide 59

Slide 59 text

©2020 Wantedly, Inc. Gradle plugin Setup

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

©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

Slide 63

Slide 63 text

©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

Slide 64

Slide 64 text

©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

Slide 65

Slide 65 text

©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

Slide 66

Slide 66 text

©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

Slide 67

Slide 67 text

©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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

©2020 Wantedly, Inc. Gradle plugin Implementation

Slide 74

Slide 74 text

©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

Slide 75

Slide 75 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

©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

Slide 79

Slide 79 text

©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

Slide 80

Slide 80 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 81

Slide 81 text

©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

Slide 82

Slide 82 text

©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

Slide 83

Slide 83 text

©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

Slide 84

Slide 84 text

©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

Slide 85

Slide 85 text

©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

Slide 86

Slide 86 text

©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

Slide 87

Slide 87 text

©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

Slide 88

Slide 88 text

©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

Slide 89

Slide 89 text

©2020 Wantedly, Inc. Types of plugin Gradle Plugin

Slide 90

Slide 90 text

©2020 Wantedly, Inc. Application module Library module id("com.android.application") id("com.android.library") Types of plugin Gradle Plugin

Slide 91

Slide 91 text

©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

Slide 92

Slide 92 text

©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

Slide 93

Slide 93 text

©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

Slide 94

Slide 94 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 95

Slide 95 text

©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

Slide 96

Slide 96 text

©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

Slide 97

Slide 97 text

©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

Slide 98

Slide 98 text

©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

Slide 99

Slide 99 text

©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

Slide 100

Slide 100 text

©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

Slide 101

Slide 101 text

©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

Slide 102

Slide 102 text

©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

Slide 103

Slide 103 text

©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

Slide 104

Slide 104 text

©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

Slide 105

Slide 105 text

©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

Slide 106

Slide 106 text

©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

Slide 107

Slide 107 text

©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

Slide 108

Slide 108 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 109

Slide 109 text

©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

Slide 110

Slide 110 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 111

Slide 111 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 112

Slide 112 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 113

Slide 113 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 114

Slide 114 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 115

Slide 115 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 116

Slide 116 text

©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().configureEach { kotlinOptions { jvmTarget = "1.8" } } } Java 8 core library desugaring Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 117

Slide 117 text

©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

Slide 118

Slide 118 text

©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

Slide 119

Slide 119 text

©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

Slide 120

Slide 120 text

©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

Slide 121

Slide 121 text

©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

Slide 122

Slide 122 text

©2020 Wantedly, Inc. my-lib/build.gradle.kts Gradle Plugin

Slide 123

Slide 123 text

©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

Slide 124

Slide 124 text

©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

Slide 125

Slide 125 text

©2020 Wantedly, Inc. Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

©2020 Wantedly, Inc. override fun apply(project: Project) { project.extensions.create("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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

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

Slide 134

Slide 134 text

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

Slide 135

Slide 135 text

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

Slide 136

Slide 136 text

©2020 Wantedly, Inc. // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } open class MyPluginOptionExtension { val jacoco: JacocoOptions = JacocoOptions() fun jacoco(action: Action) { 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

Slide 137

Slide 137 text

©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions = JacocoOptions() fun jacoco(action: Action) { 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

Slide 138

Slide 138 text

©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions = JacocoOptions() fun jacoco(action: Action) { 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

Slide 139

Slide 139 text

©2020 Wantedly, Inc. open class MyPluginOptionExtension { val jacoco: JacocoOptions = JacocoOptions() fun jacoco(action: Action) { 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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 142

Slide 142 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 143

Slide 143 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 144

Slide 144 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 145

Slide 145 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } // build.gradle.kts myPluginOptions { jacoco { isEnabled = true } } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 146

Slide 146 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 147

Slide 147 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 148

Slide 148 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 149

Slide 149 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } "debug" "release" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 150

Slide 150 text

©2020 Wantedly, Inc. private fun BaseExtension.configureJacoco(project: Project) { project.afterEvaluate { val jacocoOptions = project.extensions.getByType() .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 ) { ... } "debug" "release" "debug" "release" "flavorDebug" "flavorRelease" Gradle Plugin buildSrc/src/main/kotlin/com/mypackage/MyCustomPlugin.kt

Slide 151

Slide 151 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 152

Slide 152 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 153

Slide 153 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 154

Slide 154 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 155

Slide 155 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 156

Slide 156 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 157

Slide 157 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 158

Slide 158 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 159

Slide 159 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 160

Slide 160 text

©2020 Wantedly, Inc. private fun configureJacocoTasks( project: Project, variants: DomainObjectSet ) { variants.all { val variantName = name val isDebuggable = buildType.isDebuggable if (!isDebuggable) return@all project.tasks.register("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

Slide 161

Slide 161 text

©2020 Wantedly, Inc. Apply Gradle plugin on other modules

Slide 162

Slide 162 text

©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

Slide 163

Slide 163 text

©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

Slide 164

Slide 164 text

©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

Slide 165

Slide 165 text

©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

Slide 166

Slide 166 text

©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 { ... } buildSrc/src/build.gradle.kts Gradle Plugin

Slide 167

Slide 167 text

©2020 Wantedly, Inc. Gradle plugin Benefits

Slide 168

Slide 168 text

©2020 Wantedly, Inc. Benefits Gradle Plugin

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

©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

Slide 171

Slide 171 text

©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

Slide 172

Slide 172 text

©2020 Wantedly, Inc. Our experience Gradle Plugin

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

©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

Slide 175

Slide 175 text

©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

Slide 176

Slide 176 text

©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

Slide 177

Slide 177 text

©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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

©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

Slide 180

Slide 180 text

©2020 Wantedly, Inc. Our experience Gradle Plugin

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

©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

Slide 184

Slide 184 text

©2020 Wantedly, Inc. Summary

Slide 185

Slide 185 text

©2020 Wantedly, Inc. Summary Gradle Plugin

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

©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

Slide 189

Slide 189 text

©2020 Wantedly, Inc. Thank you @MalvinSutanto