Grit and Guts: Migrating from Groovy to Kotlin in Gradle

James Cullimore
Level Up Coding
Published in
8 min readMar 26, 2024

--

The journey of migrating your Gradle build scripts from Groovy to Kotlin in an Android project can feel akin to wielding the monstrous Dragon Slayer sword from the anime “Berserk” — daunting yet exhilarating. Much like Guts, the protagonist of “Berserk,” you’re about to face challenges that will test your resolve and skill. However, with careful planning and guidance, you can emerge victorious in this migration endeavor.

In this article, we’ll delve into the realm of Gradle migration, focusing on crucial aspects to consider throughout the process. From navigating formatting discrepancies and restructuring tasks to addressing deprecated code and optional transitioning from local.properties to gradle.properties, each step presents its own set of challenges. Additionally, we’ll explore adjustments required for Android extension blocks, adapting to Artifactory changes, and ensuring compatibility with CI/CD pipelines.

Furthermore, we’ll discuss changes to build variants, importing other Gradle scripts, and accommodating modifications in the buildscript extensions. Lastly, we’ll touch upon migrating to the TOML version catalog, providing insights to streamline your migration journey.

While this migration may invoke feelings akin to Guts’ arduous battles, remember that every step forward is a step closer to a more robust and maintainable Gradle build system. So, let’s sharpen our swords and embark on this migration quest together.

The More Known Stuff

Convert Strings, Add Parentheses, and Assignments

One of the initial hurdles in migrating from Groovy to Kotlin DSL involves converting strings, adding parentheses to method calls, and ensuring proper assignment syntax. As per the Gradle documentation, strings should be enclosed within double quotes in Kotlin DSL. Additionally, method calls must include parentheses, and assignment operations require an equal sign.

// Groovy
version = '1.0.0'
// Kotlin DSL
version = "1.0.0"

Rename File Extensions

In line with Kotlin conventions, file extensions need to be adjusted from .gradle to .gradle.kts. This simple change signifies the Kotlin DSL format and ensures compatibility with Kotlin-specific syntax highlighting and tooling.

Replace def with val or var

In Groovy, variables are often declared using the def keyword, whereas Kotlin requires explicit typing with val for immutable variables and var for mutable ones. Thus, during migration, def declarations must be replaced accordingly to adhere to Kotlin’s type safety.

// Groovy
def sdkVersion = 30
// Kotlin DSL
val sdkVersion = 30

Prefix Boolean Properties with is (sometimes)

While not always necessary, boolean properties in Kotlin conventionally start with is. However, this prefixing is context-dependent and may not apply universally. Carefully assess whether this adjustment is applicable in your specific codebase to maintain consistency and readability.

Convert Lists and Maps

Lists and maps in Groovy may require conversion to Kotlin’s collection literals. This involves replacing Groovy’s [] for lists and [:] for maps with Kotlin’s listOf() and mapOf() functions, respectively.

Configure Build Types

During migration, it’s crucial to revisit build type configurations to ensure they align with the Kotlin DSL syntax. Verify that properties and methods related to build types are appropriately translated to Kotlin, maintaining consistency in functionality and behavior across build variants.

Migrate from buildscript to plugins Block

Gradle 7.x encourages migrating from the buildscript block to the plugins block for declaring plugins. This transition ensures a cleaner and more modular build script structure, enhancing maintainability and readability.

Convert the plugins Block

Similarly, the plugins block itself requires conversion to Kotlin DSL syntax. This involves updating plugin declarations and configurations to adhere to Kotlin’s idiomatic style, promoting clarity and conciseness in the build script.

Importing and Applying Other Gradle Files:

In the realm of Gradle migration, one recurring challenge lies in importing and applying external Gradle files, especially in complex project structures. While Groovy allows for straightforward inclusion of external scripts using the apply from: syntax, Kotlin DSL presents some hurdles in this regard.

To simplify this process, it’s often recommended to centralize external configurations either in the root or app Gradle file. This consolidation not only streamlines the migration process but also enhances maintainability by providing a clear overview of all applied configurations.

Migrating from local.properties to gradle.properties

Another crucial aspect of Gradle migration involves the optional transitioning from local.properties to gradle.properties. While local.properties traditionally stores sensitive information like API keys and local configuration properties, migrating these to gradle.properties simplifies the reading and management of keys within the Kotlin DSL context.

This migration not only ensures a consistent approach to property management but also facilitates seamless integration with the Kotlin DSL syntax, enhancing readability and maintainability of the Gradle build scripts.

CI/CD Pipeline Considerations

In the context of continuous integration and deployment pipelines, it’s essential to consider the implications of Gradle migration on pipeline configurations. Specifically, ensure that CI/CD scripts check the Gradle file name for cache checksum, if applicable.

This step helps maintain cache consistency across pipeline executions, mitigating potential build inconsistencies arising from Gradle script modifications.

Addressing Deprecations and Adjustments

During Gradle migration, it’s imperative to address deprecated functionalities and make necessary adjustments to maintain compatibility and functionality. Certain elements, such as versionName, may need to be removed or replaced with alternative approaches to align with Kotlin DSL conventions.

Additionally, deprecated methods and configurations within the Gradle build scripts must be updated to utilize recommended alternatives, ensuring smooth migration and future-proofing the build system.

Utilizing Version Catalog TOML File

The migration from Groovy to Kotlin DSL also entails a shift in approach towards managing dependencies and versions. While the buildscript ext block provided a convenient mechanism for declaring and managing dependency versions in Groovy, Kotlin DSL favors a more structured approach utilizing the Version Catalog TOML file.

Leveraging Kotlin DSL for Android Build Configuration

In the process of migrating Gradle build scripts from Groovy to Kotlin DSL for Android projects, harnessing the power of Kotlin’s language features can significantly enhance the clarity and conciseness of the codebase. One effective technique involves utilizing Kotlin extension functions and type aliases to streamline Android build configurations.

Consider the following code snippet, which leverages Kotlin extension functions and type aliases to simplify access to the Android build configuration:

Understanding the Code

  • Type Alias for Android Extension: The typealias keyword is used to define a typealias AndroidExtension, which simplifies the verbose type declaration of com.android.build.api.dsl.CommonExtension<*, *, *, *, *, *>. This typealias enhances readability and reduces repetition throughout the codebase.
  • Extension Property for Android Extension: An extension property androidExtension is defined on the Project class. This property retrieves the Android build extension (CommonExtension) associated with the project’s Gradle configuration. By utilizing Kotlin’s extension properties, access to the Android build configuration is streamlined and intuitive.
  • Extension Function for Android Configuration: Another extension function android is defined on the Project class. This function takes a lambda parameter block representing the configuration block for the Android extension. Inside the function, the plugins.withType method is used to identify the presence of the Android Base Plugin, and the provided block is executed within the context of the Android extension.

Benefits and Usage:

  • Simplified Access: By encapsulating the logic for accessing and configuring the Android build extension within extension functions, the codebase becomes more expressive and concise.
  • Improved Readability: The use of type aliases and extension functions enhances code readability and maintainability, making it easier for developers to understand and modify the Gradle build scripts.
  • Consistent Configuration: With a centralized function for Android configuration (android), developers can ensure consistency in the application of Android-specific settings across multiple Gradle modules or projects.

By adopting such Kotlin-centric approaches, developers can leverage the expressive power of Kotlin DSL to streamline Android build configurations, leading to more robust and maintainable Gradle build scripts.

Thank you skydoves for the example code on your Pokedex repo.

Navigating Artifactory Migration in Kotlin DSL

As part of the Gradle migration journey, adapting Artifactory configurations to Kotlin DSL presents unique challenges and opportunities. Let’s explore the key changes required for Artifactory integration and how Kotlin DSL facilitates this transition.

Migration to Artifactory Version 5

To ensure compatibility and leverage the latest features, it’s recommended to migrate to Artifactory Version 5. This update introduces enhancements and optimizations, providing a smoother experience for managing dependencies and artifacts within the Kotlin DSL context.

Leveraging ArtifactoryPluginConvention and PublishingExtension

In Kotlin DSL, configuring Artifactory repositories and publishing artifacts can be streamlined using the ArtifactoryPluginConvention and PublishingExtension. These extensions offer convenient methods and configurations to manage artifact publication, repository authentication, and metadata generation.

Dependency Resolution Management with settings.gradle

In Kotlin DSL, the traditional resolution block in the build.gradle file is replaced by the dependencyResolutionManagement block within settings.gradle. This block allows for centralized management of dependency resolution settings across multi-module projects.

Importance of Repository Order

When configuring repositories in the dependencyResolutionManagement block, the order of repositories is crucial. For optimal dependency resolution, ensure that repositories are listed in the desired order, prioritizing local repositories like mavenLocal() before remote repositories like maven().

Retrieving Keys from local.properties

To securely retrieve sensitive information such as API keys from local.properties, Kotlin DSL offers a convenient mechanism within the dependencyResolutionManagement or pluginManagement block. Utilize the following code snippet to request keys from local.properties:

val prop = Properties().apply {
load(FileInputStream(File(rootProject.rootDir, "local.properties")))
}
println("Property:" + prop.getProperty("propertyName"))

Benefits of Kotlin DSL for Artifactory Integration

  • Simplified Configuration: Kotlin DSL provides a more intuitive and concise syntax for configuring Artifactory repositories and artifact publishing, enhancing readability and maintainability.
  • Centralized Dependency Management: With the dependencyResolutionManagement block, Kotlin DSL enables centralized management of dependency resolution settings, promoting consistency and reliability across projects.
  • Enhanced Security: Kotlin DSL facilitates secure retrieval of sensitive information from local.properties, ensuring that API keys and other credentials are handled safely within the build script.

By embracing Kotlin DSL for Artifactory integration and migration, developers can unlock the full potential of Gradle for managing dependencies and artifacts, fostering a seamless development experience.

Conclusion

The migration journey from Groovy to Kotlin DSL in Gradle scripts for Android projects demands grit, determination, and a dash of adventurous spirit, much like the legendary warrior Guts from “Berserk.” Throughout this endeavor, we’ve encountered challenges and triumphs, navigating through formatting discrepancies, restructuring tasks, and adapting to new conventions.

With Kotlin DSL, we’ve unlocked a realm of possibilities, streamlining configurations, simplifying dependencies, and enhancing readability. From leveraging extension functions to managing Artifactory integrations, Kotlin has proven to be a formidable ally in our quest for Gradle migration mastery.

As we reflect on our journey, let us remember the lessons learned and the victories achieved. While the path may have been arduous at times, the rewards of cleaner, more maintainable build scripts and the empowerment of Kotlin’s expressive syntax make every challenge worthwhile.

So, as we raise our metaphorical swords to the sky, let us embrace the courage and tenacity that guided us through this migration. With Guts-like determination, we continue forward, forging a brighter future for our Android projects powered by the resilience of Kotlin DSL and the indomitable spirit of innovation.

--

--