Writing gradle scripts using Kotlin

Writing gradle scripts using Kotlin

summary

IDE support

Build importSyntax highlighting 1Semantic editor 2
IntelliJ IDEA
Android Studio
Eclipse IDE
CLion
Apache NetBeans
Visual Studio Code (LSP)
Visual Studio
  1. Kotlin syntax highlighting in Gradle Kotlin DSL scripts
  2. Gradle Kotlin DSL scripts supports code completion, navigation to source code, document viewing, refactoring, etc;

Kotlin DSL script naming

name

Groovy DSL script file usage gradle extension

Kotlin DSL script file usage gradle.kts extension file name

Activate and use kotlin dsl

  • Name the script file gradle.kts can be activated. Also applicable to settings file (settings.gradle.kts) and initialization scripts.
  • For better IDE support, the following naming convention is recommended:
    • The settings script is named * settings.gradle.kts (including all scripts introduced from the settings script)
    • initialization scripts Press * init.gradle.kts is named after the naming pattern, or simply named init gradle. kts.
  • The kotlin DSL build script implicitly imports the following contents:

Read runtime properties from kotlin

Gradle Kotlin DSL Primer

gradle has two runtime properties:

  1. project properties
  2. extra properties

Project properties

You can access through kotlin's proxy properties:

build.gradle.kts

// Attribute must exist
val myProperty: String by project  
// Property can not exist
val myNullableProperty: String? by project 

extra attribute

The extra attribute is implemented anywhere ExtensionAware All objects of the interface can be accessed;

build.gradle.kts

val myNewProperty by extra("initial value")   // ❶
val myOtherNewProperty by extra { "calculated initial value" }   //❷ 

val myProperty: String by extra   // ❸
val myNullableProperty: String? by extra   // ❹

❶ create a new extra property named myNewProperty in the current context (currently in project) and initialize its value as "initial value"

❵the same as ❶, but the initial value of the attribute is calculated through the lamdba expression;

❸ bind the property in the current context (currently project) to the myProperty property property;

❹ is the same as ❸, but the allowed value is null;

Access rootProject properties in subprojects

val myNewProperty: String by rootProject.extra

By is the keyword of kotlin, which means provided by, that is, the attribute is represented by some other object

Defining and using attributes in tasks

The task also inherits ExtensionAware, so we can also use the extra attribute in the task

tasks {
    test {
        val reportType by extra("dev")  
        doLast {
            // Use 'suffix' for post processing of reports
        }
    }

    register<Zip>("archiveTestReports") {
        val reportType: String by test.get().extra  
        archiveAppendix.set(reportType)
        from(test.get().reports.html.destination)
    }
}

Define and access extra attributes through map format

extra["myNewProperty"] = "initial value"   // ❶

tasks.create("myTask") {
    doLast {
        println("Property: ${project.extra["myNewProperty"]}")  // ❷ 
    }
}

Migrate gradle build logic to Kotlin

Reference documents:

  1. Migrating build logic from Groovy to Kotlin (gradle.org)
  2. Android Gradle scripts migrated from Groovy to Kotlin DSL - Paladin wind - blog Park (cnblogs.com)

Preparing groovy scripts

  1. The quotation marks are unified into double quotation marks
  2. Method call with parentheses
  3. Assignment plus equal sign

Uniform quotation marks

  • Use the ⌘ ⇧ R shortcut key to call up the search and replace tool window, and set the file matching to gradle, and then replace all single quotes with double quotes.

  • When finished, use the gradle file again to synchronize the project to see if there are any errors

Assignment and attribute modification

  • Add an equal sign to all assignment operations
  • Add parentheses to all function call operations

Here we need to adjust according to the actual situation.

rename file

Will Rename the gradle file to gradle.kts, you can complete the rename operation through the following command

find . -name "*.gradle" -type f | xargs -I {} mv {} {}.kts

Other common modifications

Access the gradle property in the kotlin rootProject script

allprojects {
    repositories {
        maven {
            name = "Sonatype-Snapshots"
            setUrl("https://oss.sonatype.org/content/repositories/snapshots")
            credentials(PasswordCredentials::class.java) {
                username = property("ossrhUsername").toString()
                password = property("ossrhPassword").toString()
            }
        }
        google()
        jcenter()
        mavenCentral()
    }
}

Define task

tasks.register("clean", Delete::class.java) {
    group = "build"
    delete(rootProject.buildDir)
}

Migrating configuration instances

rootProject kotlin dsl build script template

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    // Define extra attribute
    val kotlin_version by extra("1.4.32")
    val android_gradle_build_version by extra("4.1.3")
    repositories {
        maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
        maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
        maven { setUrl("https://maven.aliyun.com/repository/google") }
        maven { setUrl("https://maven.aliyun.com/repository/public") }
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:$android_gradle_build_version")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
        maven { setUrl("https://maven.aliyun.com/repository/google") }
        maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
        maven { setUrl("https://maven.aliyun.com/repository/public") }
        // Join the temporary warehouse of the project to facilitate the test
        maven {
            name = "ProjectLocal-Snapshots"
            setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}snapshots"))
        }
        maven {
            name = "ProjectLocal-Release"
            setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}release"))
        }

        // Join maven snapshot warehouse and release warehouse
        maven {
            name = "Sonatype-Snapshots"
            setUrl("https://oss.sonatype.org/content/repositories/snapshots")
        }
        maven {
            name = "Sonatype-Staging"
            setUrl("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
            credentials(PasswordCredentials::class.java) {
                username = property("ossrhUsername").toString()
                password = property("ossrhPassword").toString()
            }
        }
        google()
        mavenCentral()
        jcenter()
    }
}

tasks.register("clean", Delete::class.java) {
    group = "build"
    delete(rootProject.buildDir)
}

Module build Gradle module build script template

plugins {
    id("com.android.library")
    id("signing")
    // Equivalent to id("")
    `maven-publish`
    kotlin("android")
    kotlin("android.extensions")

    // Introducing third-party Gradle plug-ins
    id("com.github.hanlyjiang.android_maven_pub") version ("0.0.9") apply (false)
}

android {
    compileSdkVersion(30)
    buildToolsVersion("30.0.3")

    defaultConfig {
        minSdkVersion(22)
        targetSdkVersion(30)
        versionCode(1)
        versionName("1.0.0")

        testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner")
        consumerProguardFiles("consumer-rules.pro")
    }

    buildTypes {
        getByName("release") {
            minifyEnabled(false)
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

    compileOptions {
        sourceCompatibility(JavaVersion.VERSION_1_8)
        targetCompatibility(JavaVersion.VERSION_1_8)
    }
}

dependencies {
    implementation("org.jetbrains:annotations:21.0.1")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.3")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
    
    // StringRes annotation
    implementation("androidx.appcompat:appcompat:1.3.1")
    // Annotation Library https://developer.android.com/jetpack/androidx/releases/annotation#annotation-1.2.0
    implementation("androidx.annotation:annotation:1.2.0")
}

// Create custom task
tasks.create("showGitRepoInfo") {
    group = "help"
    doLast {
        println("${getGitBranch()}/${getGitRevision()}")
    }
}

// spread function 
fun String.execute(): String {
    val process = Runtime.getRuntime().exec(this)
    return with(process.inputStream.bufferedReader()) {
        readText()
    }
}

/**
 * Get git revision with work tree status
 *
 * @return
 */
fun getGitRevision(): String {
    val rev = "git rev-parse --short HEAD".execute().trim()
    val stat = "git diff --stat".execute().trim()
    return if (stat.isEmpty()) {
        rev
    } else {
        "$rev-dirty"
    }
}

/**
 * Get git branch name
 *
 * @return
 */
fun getGitBranch(): String {
    return "git rev-parse --abbrev-ref HEAD".execute().trim()
}

Limitations / deficiencies

It is inconvenient to introduce other independent kotlin dsl scripts into kotlin dsl scripts. There will be the problem of unable to identify related dependent objects. In some cases, you still have to import gradle scripts written in groovy.

Keywords: Android Gradle kotlin

Added by npereira on Thu, 24 Feb 2022 17:17:49 +0200