Migrating build configurations from Groovy to KTS

Migrating build configurations from Groovy to KTS

icon.jpg

foreword

As Androida developer, I am used to object-oriented programming, and I am used to IDEAvarious auxiliary development shortcut functions provided.

Well, scripts with unfamiliar, conventional syntax Groovyhave always been my thing.

Kotlin DSLThe emergence of feels tailor-made for us, because code written in Kotlin is more readable, and Kotlin provides better compile-time checking and IDE support.


Noun concept explanation

The terms "KTS" and "Kotlin DSL" are used interchangeably when discussing migration from Groovy. In other words, "convert an Android project from Groovy to KTS" and "convert an Android project from Groovy to Kotlin DSL" actually mean the same thing.

Comparison between Groovy and KTS

type Kotlin Groovy
Automatic code completion support not support
Is it type safe yes no
Source code navigation support not support
refactor automatic association manual modification

advantage:

  • Can be used Kotlin, developers may be more familiar with this language and prefer it.
  • IDEBetter support, autocompletion hints, refactoring, importsetc.
  • Type Safety: KotlinIt is statically typed.
  • There is no need to migrate all at once: scripts in the two languages ​​can coexist and call each other.

Cons and Known Issues:

  • Currently, the build speed KTSof may be Groovyslower than that of adopting (about 40% (about 8s) of self-test small demo time-consuming).

  • Project StructureThe editor does not expand constants defined in buildSrcfolders for library names or versions.

  • KTSFiles currently do not provide text hints in the project view .

Android build configuration migration from Groovy to KTS

Preparation

  1. GroovyStrings can be quoted with single 'string'or double quotes "string", while double quotes Kotlinare required "string".

  2. GroovyParentheses are allowed to be omitted when calling a function, whereas parentheses are Kotlinalways required.

  3. Gradle Groovy DSLIt is allowed to omit the =assignment , whereas Kotlinan assignment operator is always required.

So KTSyou need to do it uniformly in:

  • Use double quotes to unify quotes.

groovy-kts-diff1.png

  • Disambiguate function calls and property assignments (using parentheses and assignment operators, respectively).

groovy-kts-diff2.png

script file name

Groovy DSL script files use .gradlethe file extension.

Kotlin DSL script files use .gradle.ktthe s file extension.

Migrate files one at a time

Since you can combine Groovy buildfiles ,KTS build an easy way to convert your project to is to first select a simple file (for example ), rename it to , and convert its contents to . Afterwards, make sure your project still compiles after migrating each file .KTSbuildsettings.gradlesettings.gradle.ktsKTSbuild

Custom Task

Since Koltinit is a statically typed language and Groovya dynamic language, the former is type-safe, and the difference in their nature is clearly reflected in the creation and configuration of tasks. For details, please refer to the official Gradle migration tutorial

// groovy
task clean(type: Delete) {
    
    
    delete rootProject.buildDir
}
// kotiln-dsl
tasks.register("clean", Delete::class) {
    
    
    delete(rootProject.buildDir)
}
val clean by tasks.creating(Delete::class) {
    
    
    delete(rootProject.buildDir)
}
open class GreetingTask : DefaultTask() {
    
    
    var msg: String? = null
    @TaskAction
    fun greet() {
    
    
        println("GreetingTask:$msg")
    }
}
val msg by tasks.creating(GreetingTask::class) {
    
    }
val testTask: Task by tasks.creating {
    
    
   doLast {
    
    
       println("testTask:Run")
   }
}
val testTask2: Task = task("test2") {
    
    
    doLast {
    
     
      println("Hello, World!") 
    }
}
val testTask3: Task = tasks.create("test3") {
    
    
    doLast {
    
    
        println("testTask:Run")
    }
}

Use pluginscode blocks

If you builduse pluginscode , IDEyou'll be able to get contextual information even when the build fails. IDEThis information can be used to perform code completion and provide other helpful suggestions to help you resolve issues with KTSyour files .

In your code, apply pluginreplace with declarative pluginsblocks. GroovyThe following code in...

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'

becomes the following code in KTS:

plugins {
    
    
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-kapt")
    id("androidx.navigation.safeargs.kotlin")
 }

For more information on pluginscode blocks, see Gradle's migration guide .

Note : pluginsCode blocks only resolve plugins provided in the Gradle Plugin Portal or in custom repositories specified with pluginManagementcode blocks. If plugins come from buildScriptdependencies , then those plugins must be used in Kotlin applyto be applied. For example:

apply(plugin = "kotlin-android")
apply {
    
    
    from("${
      
      rootDir.path}/config.gradle")
    from("${
      
      rootDir.path}/version.gradle.kts")
}

See the Gradle documentation for details .

It is strongly recommended that you plugins {}use blocks in preference to apply()functions.

There are two key best practices to make it Kotlin DSLeasier to work in the static context of :

  • use plugins {}blocks
  • Put your local build logic in your build's buildSrc directory

The plugins {} block is about keeping your build scripts declarative to take full advantage Kotlin DSL.

Using the buildSrc project is about organizing your build logic into shared native plugins and conventions that are easy to test and have good IDE support.

dependency management

common dependencies

// groovy
implementation project(':library')
implementation 'com.xxxx:xxxx:8.8.1'

// kotlin
implementation(project(":library"))
implementation("com.xxxx:xxx:8.8.1")

freeTree

// groovy
implementation fileTree(include: '*.jar', dir: 'libs')

//kotlin
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))

special type library dependencies

//groovy
implementation(name: 'splibrary', ext: 'aar')

//kotlin
implementation (group="",name="splibrary",ext = "aar")

build variant

explicit and implicitbuildTypes

In the Kotlin DSL, some buildTypes(like debugand release,) are provided implicitly. However, buildTypesothers must be created manually.

For example, in Groovy you might have debug, releaseand staging buildTypes:

buildTypes
  debug {
    
    
    ...
  }
  release {
    
    
    ...
  }
  staging {
    
    
    ...
  }

In KTS, only debugand release buildTypesare provided implicitly, stagingwhile must be created manually by you:

buildTypes
  getByName("debug") {
    
    
    ...
  }
  getByName("release") {
    
    
    ...
  }
  create("staging") {
    
    
    ...
  }

for example

Grovvywrite:

productFlavors {
    
    
        demo {
    
    
            dimension "app"
        }
        full {
    
    
            dimension "app"
            multiDexEnabled true
        }
    }

buildTypes {
    
    
        release {
    
    
            signingConfig signingConfigs.signConfig
            minifyEnabled true
            debuggable false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
    
    
            minifyEnabled false
            debuggable true
        }
    }
signingConfigs {
    
    
        release {
    
    
            storeFile file("myreleasekey.keystore")
            storePassword "password"
            keyAlias "MyReleaseKey"
            keyPassword "password"
        }
        debug {
    
    
            ...
        }
    }

kotlin-KTLwrite:

productFlavors {
    
    
    create("demo") {
    
    
        dimension = "app"
    }
    create("full") {
    
    
        dimension = "app"
        multiDexEnabled = true
    }
}

buildTypes {
    
    
        getByName("release") {
    
    
            signingConfig = signingConfigs.getByName("release")
            isMinifyEnabled = true
            isDebuggable = false
            proguardFiles(getDefaultProguardFile("proguard-android.txtt"), "proguard-rules.pro")
        }
        
        getByName("debug") {
    
    
            isMinifyEnabled = false
            isDebuggable = true
        }
    }
    
signingConfigs {
    
    
        create("release") {
    
    
            storeFile = file("myreleasekey.keystore")
            storePassword = "password"
            keyAlias = "MyReleaseKey"
            keyPassword = "password"
        }
        getByName("debug") {
    
    
            ...
        }
    }

access configuration

gradle.properties

We usually write signature information, version information and other configurations in gradle.properties. In kotlin-dsl, we can access them in the following ways:

  1. rootProject.extra.properties
  2. project.extra.properties
  3. rootProject.properties
  4. properties
  5. System.getProperties()

System.getProperties()There are more restrictions on the use

  • Parameter names must follow systemProp.xxxthe format (eg: systemProp.kotlinVersion=1.3.72);
  • It is related to the currently executed task ( > Configure project :buildSrcand > Configure project :the result is different, the latter cannot obtain gradle.propertiesthe data in the task);

local.properties

Get project local.propertiesfiles

gradleLocalProperties(rootDir)

gradleLocalProperties(projectDir)

Get the value of the system environment variable

val JAVA_HOME:String = System.getenv("JAVA_HOME") ?: "default_value"

About Ext

A Gradle configuration best practice officially recommended by Google is extto define project-wide properties in the code block of the outermost build.gradle file of the project , and then share these properties among all modules. For example, we usually store the version number of dependencies in this way.

// build.gradle

ext {
    
    
    compileSdkVersion = 28
    buildToolsVersion = "28.0.3"
    supportLibVersion = "28.0.0"
    ...
}

However, due to the lack of IDE assistance (jump view, global refactoring, etc. are not supported), the actual use experience is not good.

usedKTL in place of inextraGroovyext

// The extra object can be used for custom properties and makes them available to all
// modules in the project.
// The following are only a few examples of the types of properties you can define.
extra["compileSdkVersion"] = 28
// You can also create properties to specify versions for dependencies.
// Having consistent versions between modules can avoid conflicts with behavior.
extra["supportLibVersion"] = "28.0.0"
android {
    
    
    // Use the following syntax to access properties you defined at the project level:
    // rootProject.extra["property_name"]
    compileSdkVersion(rootProject.extra["sdkVersion"])

    // Alternatively, you can access properties using a type safe delegate:
    val sdkVersion: Int by rootProject.extra
    ...
    compileSdkVersion(sdkVersion)
}
...
dependencies {
    
    
    implementation("com.android.support:appcompat-v7:${
      
      rootProject.ext.supportLibVersion}")
    ...
}

build.graldeThe data in extis accessible build.gradle.ktsusing in extra.

Modify the name of the generated apk and add the cpu architecture supported by the apk in BuildConfig

val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)
android.applicationVariants.all {
    
    
    val buildType = this.buildType.name
    val variant = this
    outputs.all {
    
    
        val name =
            this.filters.find {
    
     it.filterType == com.android.build.api.variant.FilterConfiguration.FilterType.ABI.name }?.identifier
        val baseAbiCode = abiCodes[name]
        if (baseAbiCode != null) {
    
    
          	//写入cpu架构信息
            variant.buildConfigField("String", "CUP_ABI", "\"${
      
      name}\"")
        }
        if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
    
    
            //修改apk名称
            if (buildType == "release") {
    
    
                this.outputFileName = "KotlinDSL_${
      
      name}_${
      
      buildType}.apk"
            } else if (buildType == "debug") {
    
    
                this.outputFileName = "KotlinDSL_V${
      
      variant.versionName}_${
      
      name}_${
      
      buildType}.apk"
            }
        }
    }
}

buildSrc

When we use Groovylanguage construction, we often extract one version_config.gradleas a global variable control, and extthe extension function must be used. In our system Gradle Kotlin DSL, if we want to use global control, we need to recommend it buildSrc.

Complex build logic is often well suited for packaging as custom tasks or binary plugins. Custom tasks and plugin implementations should not exist in build scripts. buildSrcThen you don't need to share the code between multiple independent projects, and you can use the code very conveniently.

buildSrcTreated as a build directory. Once the compiler discovers the directory, Gradleit automatically compiles and tests this code and puts it in the build script's classpath.

  1. Create a directory first buildSrc;
  2. Create files in this directory build.gradle.kts;
  3. Create a buildSrc/src/main/koltindirectory;
  4. Create a file in this directory Dependencies.ktas a version management class;

Note buildSrcthat build.gradle.kts:

plugins {
    
    
    `kotlin-dsl`
}
repositories {
    
    
    jcenter()
}

or

apply {
    
    
    plugin("kotlin")
}
buildscript {
    
    
    repositories {
    
    
        gradlePluginPortal()
    }
    dependencies {
    
    
        classpath(kotlin("gradle-plugin", "1.3.72"))
    }
}
//dependencies {
    
    
//    implementation(gradleKotlinDsl())
//    implementation(kotlin("stdlib", "1.3.72"))
//}
repositories {
    
    
    gradlePluginPortal()
}

File execution order buildSrcbetween different versions :build.gradle

gradle-wrapper.properties:5.6.4

com.android.tools.build:gradle:3.2.0

  1. BuildSrc:build.gradle
  2. setting.gradle
  3. Project:build.gradle
  4. Moudle:build.gradle

gradle-wrapper.properties:6.5

com.android.tools.build:gradle:4.1.1

  1. setting.gradle
  2. BuildSrc:build.gradle
  3. Project:build.gradle
  4. Moudle:build.gradle

Therefore, we need to pay attention to the loading order of files buildSrcin non-directories .build.gradle.ktsDependencies.kt

reference documents

Android Official Website - Migrate build configuration from Groovy to KTS

Migrating build logic from Groovy to Kotlin

GitHub:kotlin-dsl-samples

GitHub:kotlin-dsl-samples/samples/hello-android

Kotlin DSL: Gradle scripts in Android made easy

buildSrc Official Documentation

Gradle’s Kotlin DSL BuildSrc

The article is all told here, if you have other needs to communicate, you can leave a message~! ~!

If you want to read more articles by the author, you can check out my personal blog and public account:
Revitalize Book City

Guess you like

Origin blog.csdn.net/stven_king/article/details/118310535