Gradle Plugin Configuration using Extensions

Gradle provides developers with immense flexibility for customizing their build scripts. To make the configuration process more organized and user-friendly, Gradle plugins often leverage Extensions. These Extensions enable users to encapsulate build configurations and provide a structured way to modify the plugin’s behavior in both Kotlin and Groovy build scripts. In this blog post, we’ll explore how to support configuration in Kotlin Gradle files using Extensions within a Gradle plugin.

What are Gradle plugins?

Gradle plugins serve as modular components that expand the capabilities of the Gradle build system. You can harness this flexibility to craft your own plugins, allowing you to encapsulate your distinctive build logic and apply it within your build scripts (see Gradle build lifecycle).

Gradle offers the Plugin Portal, where you can explore a diverse array of community-contributed plugins. Furthermore, you have the capability to develop custom plugins to fine-tune your build process based on your precise requirements. This emphasis on plugins greatly advances automation and uniformity within your projects.

What are Gradle Extensions?

Gradle Extension are custom objects or classes that are exposed by a Gradle plugin, allowing users to configure the plugin’s behavior. By defining Extensions, the plugin author can provide a clean, domain-specific API that simplifies the build script and enhances readability. Users can access these Extensions to set various properties and values, tailoring the plugin’s functionality to their specific requirements.

Extension Scope

The Extension instance is project-specific. When the plugin is applied to multiple projects within a multi-project build, each project will have its own instance of the Extension. This ensures that configuration changes are isolated and only apply to the specific project where they were made.

Default Values

When defining an Extension class, you can set default values for its properties. These default values are automatically applied to the Extension instance during registration. If a user does not explicitly set a property in their build script, it will use the default value defined in the Extension class.

Validation and Type Safety

Gradle performs validation and type safety checks when applying configurations to the Extension instance. If a user tries to set a property with an incompatible type or value, Gradle will raise an error or warning, ensuring that the configuration is correctly handled and errors are caught early.

Creating a Gradle Plugin with Extension Support

At Contentsquare, we created a Gradle plugin for our Crash Reporter feature, allowing our clients to effortlessly upload ProGuard mapping files for obfuscated builds. This enables us to deobfuscate any crashes that make their way to our backend servers. To allow our clients to configure the plugin, we’ve exposed the ErrorAnalysisCrashExtension. Detailed information about this plugin and its configuration is available in our public documentation.

Create a Gradle Plugin Project

To get started, set up a new Gradle plugin project or add the extension support to an existing one (see Gradle custom plugin creation).

Define the Extension

In Kotlin, define a data class to represent the Extension’s configuration properties (see Gradle Extension).

open class ErrorAnalysisCrashExtension {

    companion object {
        const val CRASH_EXTENSION_NAME = "errorAnalysisCrash"
    }

    var uploadCredentials: UploadMappingFileCredentials? = null

    fun uploadCredentials(action: UploadMappingFileCredentials.() -> Unit) {
        if (uploadCredentials == null) {
            uploadCredentials = UploadMappingFileCredentials()
        }

        uploadCredentials?.run(action)
    }
}

open class UploadMappingFileCredentials {
    var clientSecret: String? = null
    var clientId: String? = null
    var projectId: Int? = null
    var uploadUrl: String? = null
}

Register the Extension

In your plugin’s implementation file, register the Extension to make it accessible to build scripts:

class ErrorAnalysisCrashPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.pluginManager.withPlugin(ANDROID_APP_PLUGIN) {
            val androidExt = project.extensions.getByType(AppExtension::class.java)
            registerExtensions(androidExt)
        }
    }

    private fun registerExtensions(androidExt: AppExtension) {
        val registerExtension: ExtensionAware.() -> Unit = {
            extensions.add(
                ErrorAnalysisCrashExtension.CRASH_EXTENSION_NAME,
                ErrorAnalysisCrashExtension::class.java
            )
        }

        androidExt.buildTypes.all { registerExtension() }
        androidExt.productFlavors.all { registerExtension() }
    }
}

Using the Extension

The uploadCredentials method in the ErrorAnalysisCrashExtension class enables users to configure upload credentials for mapping files in Gradle build scripts using Kotlin DSL-style configuration like:

errorAnalysisCrash {
    uploadCredentials {
        clientSecret = "yourSecret"
        clientId = "yourClientID"
        projectId = 123
        uploadUrl = "https://example.com/upload"
    }
}

Any of your Gradle tasks can then access the Extension and configure themselves accordingly.

Conclusion

Leveraging Extensions in your Gradle plugin provides a seamless and consistent way for users to customize build configurations in both Kotlin and Groovy build scripts. These Extensions serve as a well-defined API, enhancing the readability and maintainability of your plugin’s configuration. By following the steps outlined in this blog post, you can empower your users to effortlessly tailor the behavior of your Gradle plugin to suit their specific needs. Happy building!