DEV Community

Cover image for How to Convert Android Gradle Groovy to KTS?
Vincent Tsen
Vincent Tsen

Posted on • Updated on • Originally published at vtsen.hashnode.dev

How to Convert Android Gradle Groovy to KTS?

Step-by-step guide to convert or migrate your Android project's Gradle script from Groovy script to Kotlin script (KTS)

Introduction

Gradle is the build automation tool. Android Studio uses Gradle to build the apps. Gradle can be written in these 2 domain-specify languages (DSL):

  • Groovy Script

  • Kotlin Script (KTS)

What is Groovy?

Groovy is a dynamically typed language, which means the variable types are known at run time. This is usually an interpreter or scripting language.

What is Kotlin?

Kotlin is a statically typed language, which means the variable types are known at compile time. This is usually for general-purpose programming language. However, for this Gradle building context, Kotlin is considered as a script. It is called Kotlin Script (KTS)

Both Groovy and Kotlin can run on Java Virtual Machine (JVM).

Why Convert Groovy to KTS?

This is what official documentation says:

In the future, KTS will be preferred over Groovy for writing Gradle scripts because Kotlin is more readable and offers better compile-time checking and IDE support.

But it also says:

builds using KTS tend to be slower than builds using Groovy

It sounds to me, it is not ready yet? Probably that's the reason why the default Gradle script setup from Android Studio is Groovy and not KTS.

If build performance is not an issue, maybe you can consider migrating to KTS? But who doesn't want a faster build?

So I think conversion to KTS is probably for education purposes or preparing yourself for the future. The following guide provides step-by-step instructions on what I did to convert my template app to use KTS.

Step-by-step Guide

  • *.gradle extension is for Groovy script

  • *.gradle.kts extension is for Kotlin script.

1. Rename settings.gradle to settings.gradle.kts

You get the following error.

Function invocation 'include(...)' expected

To fix that, you change

include ':app'
Enter fullscreen mode Exit fullscreen mode

to

include ("app")
Enter fullscreen mode Exit fullscreen mode

You get this message on the top of your IDE.

Multiple script definitions are applicable to this script. KotlinSettingScript is used

It looks like a known issue, but it is not a show-stopper.

You also have this message which will be gone after you convert all the Groovy scripts to KTS.

Code insight unavailable (script configuration wasn't received) - Add to standalone scripts

2. Rename build.gradle to build.gradle.kts

You get this error:

Unexpected tokens (use ';' to separate expressions on the same line)

To fix that, you change

task clean(type: Delete) {
    delete rootProject.buildDir
}
Enter fullscreen mode Exit fullscreen mode

to

tasks {
    register("clean", Delete::class) {
        delete(rootProject.buildDir)
    }
}
Enter fullscreen mode Exit fullscreen mode

[Updated - Jan 3, 2023]: It looks like this "clean" task is no longer needed because it has been implemented by default in Gradle. So I have removed this task in build.gradle / build.gradle.kts.

After that, you compile and get the same error:

Unexpected tokens (use ';' to separate expressions on the same line)

To fix that, you replace

buildscript {
    ext {
        android_gradle_plugin_version = '7.2.2'
    }
}

plugins {
    id 'com.android.application' version "$android_gradle_plugin_version" apply false
    id 'com.android.library' version "$android_gradle_plugin_version" apply false
    id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}
Enter fullscreen mode Exit fullscreen mode

with

buildscript {
    val android_gradle_plugin_version by extra("7.2.2")
}

plugins {
    id("com.android.application") version "${extra["android_gradle_plugin_version"]}" apply false
    id("com.android.library") version "${extra["android_gradle_plugin_version"]}" apply false
    id("org.jetbrains.kotlin.android") version "1.7.0" apply false
}
Enter fullscreen mode Exit fullscreen mode

It can build and run successfully. However, there is still this error message:

val ExtensionAware.extra: ExtraPropertiesExtension' can't be called in this context by implicit receiver. Use the explicit one if necessary

It looks like this is plugins DSL limitations - constrained syntax which doesn't allow you to access the variable in the plugins block - does not support arbitrary code.

The plugins {} block does not support arbitrary code. It is constrained...

So just hard code it instead:

buildscript {
}

plugins {
    id("com.android.application") version "7.2.2" apply false
    id("com.android.library") version "7.2.2" apply false
    id("org.jetbrains.kotlin.android") version "1.7.0" apply false
}
Enter fullscreen mode Exit fullscreen mode

FYI - I don't have the compose_version variable here because I've moved it to the app\build.gradle

3. Rename app\build.gradle to app\build.gradle.kts

If you use the "Refactor -> Rename...", it automatically updates the comment in proguard-rules.pro file.

You will get errors, and here are the fixes.

Change plugins

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
Enter fullscreen mode Exit fullscreen mode

to

plugins {
    id ("com.android.application")
    id ("org.jetbrains.kotlin.android")
}
Enter fullscreen mode Exit fullscreen mode

Change compile Sdk & defaultConfig

android {
    compileSdk 32

    defaultConfig {
        applicationId "vtsen.hashnode.dev.newemptycomposeapp"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

to

android {
    compileSdk = 32

    defaultConfig {
        applicationId = "vtsen.hashnode.dev.newemptycomposeapp"
        minSdk = 21
        targetSdk = 32
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

Change buildTypes and compleOptions

android {
    /*...*/
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(
                'proguard-android-optimize.txt'), 
                    'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

to

android {
    /*...*/
    buildTypes {
        release {
            isMinifyEnabled  = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                    "proguard-rules.pro")
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

Note: minifyEnabled is renamed to isMinifyEnabled

Change buildFeatures and composeOptions

android {
    /*...*/
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.2.0'
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

to

android {
    /*...*/
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.2.0"
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

Change packagingOptions and namespace

android {
    /*...*/
    packagingOptions {  
        resources {  
            excludes += '/META-INF/{AL2.0,LGPL2.1}'  
        }  
    }

    namespace 'vtsen.hashnode.dev.newemptycomposeapp'
}
Enter fullscreen mode Exit fullscreen mode

to

android {
    /*...*/
    packagingOptions {  
        resources {  
            excludes.add("/META-INF/{AL2.0,LGPL2.1}")  
        }  
    }

    namespace = "vtsen.hashnode.dev.newemptycomposeapp"
}
Enter fullscreen mode Exit fullscreen mode

Change dependencies

dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
    implementation 'androidx.activity:activity-compose:1.5.1'

    def compose_version = '1.2.1'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"

    implementation "com.google.accompanist:accompanist-systemuicontroller:0.24.2-alpha"
}
Enter fullscreen mode Exit fullscreen mode

to

dependencies {  

    implementation("androidx.core:core-ktx:1.8.0")  
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")  
    implementation("androidx.activity:activity-compose:1.5.1")  

    val composeVersion = "1.2.1"  
    implementation("androidx.compose.ui:ui:$composeVersion")  
    implementation("androidx.compose.material:material:$composeVersion")  
    implementation("androidx.compose.ui:ui-tooling-preview:$composeVersion")  
    debugImplementation("androidx.compose.ui:ui-tooling:$composeVersion") 

    implementation("com.google.accompanist:accompanist-systemuicontroller:0.24.2-alpha")  
}
Enter fullscreen mode Exit fullscreen mode

composeVersion is used instead of compose_version due to the naming convention in Kotlin

Build Time Comparisons

I perform a quick built-time testing, the difference is not significant. Both build times are about the same. Probably this project is too simple, so the difference is not obvious.

Build Time First Compile (clean build) Second Compile (clean build)
Groovy 6 seconds 2-3 seconds
KTS 6 seconds 2-3 seconds

Source Code


Originally published at https://vtsen.hashnode.dev.

Top comments (4)

Collapse
 
nomanbaig profile image
Noman Baig

A build operation failed.
Could not create task ':app:processDebugResources'.
Could not create task ':app:processDebugResources'.
Cannot use @TaskAction annotation on method IncrementalTask.taskAction$gradle_core() because interface org.gradle.api.tasks.incremental.IncrementalTaskInputs is not a valid parameter to an action method.

Collapse
 
vtsen profile image
Vincent Tsen

Mine is okay. It could be due to the older version of Android Studio. I'm on Android Studio Flamingo.

Collapse
 
devhunter2024 profile image
dev

Thanks, this article save me!!

Collapse
 
vtsen profile image
Vincent Tsen

I'm glad it helps.