Hi, i have a question related to `ExtensionContain...
# gradle
s
Hi, i have a question related to
ExtensionContainer
while creating a custom gradle plugin on android. In order to create an extension at the project scope i do this:
Copy code
project.extensions.create(
    "myExtension",
    MyExtension::class.java
)
Is there a way i can create an extension that is scoped to applicationVariants so i can configure the variants like this:
Copy code
android.applicationVariants.configureEach { variant ->
    myOtherExtension {
       // configuration
    }
}
v
Practically any object created by Gradle is extension aware, so while I'm not an Android developer I'd say most probably yes, you can. You might need to cast the variant to
ExtensionAware
though if it does not decalare it.
s
Would i need to find the extension by name or type e.g
Copy code
configure<MyExtensionClass>{
   // configuration
}
I was hoping I could get it more ergonomically directly as
Copy code
myOtherExtension {
  // configuration
}
v
As I said, I'm not an Android developer, so I don't know. But if there are no accessors generated, you an anytime provide the necessary accessors for Kotlin DSL yourself. Only drawback, if you add your extension optionally, or if your plugin is in the classpath but not applied, you still have access to the accessors.
s
Alright, that make sense. Thanks for the help 🙏
a
If you’re using the Kotlin DSL, when you add an extension to a Gradle managed type, then Gradle will generate a Kotlin DSL accessor for that specific type. However, there are a few quirks that make this tricky. My guess is that
android.applicationVariants
is a
NamedDomainObjectContainer<T>
, and you’re adding extensions to every
T
.
Copy code
android.applicationVariants.configureEach { variant ->
  // all types that Gradle instantiate are ExtensionAware, but you might need to manually cast to tell the Kotlin compiler
  //require(variant is ExtensionAware)
   variant.extensions.create(
       "myExtension",
       MyExtension::class.java
   )
}
And then in code you can configure the extensions
Copy code
android.applicationVariants.configureEach { variant ->
    variant.configure<MyExtension> { // retrieve and configure MyExtension, failing if it doesn't exist
       // configuration
    }
}
However, just because every
T
has the same extension, that doesn’t mean Gradle will generate a generic DSL accessor! So Gradle might* generate a DSL accessor for every specific element (*as I said, it’s tricky, there are some caveats I’m not explaining right now), but that doesn’t mean it will generate an accessor that you can use like this:
Copy code
// build.gradle.kts

android.applicationVariants.configureEach {
    myExtension {
       // configuration
    }
}
Of course in Groovy you can do that, because Groovy lets you do anything (even if it’s wrong). To work around it you could define your own Kotlin DSL function.
Copy code
fun ApplicationVariant.myExtension(configure: MyExtension.() -> Unit) {
  variant.configure<MyExtension>(configure)
}
If you wanted to be clever/risky/sneaky you could even put that function in an implicitly imported package, so it would be available automatically to users
Copy code
@file:Suppress("PackageDirectoryMismatch")
package org.gradle.kotlin.dsl

fun ApplicationVariant.myExtension(configure: MyExtension.() -> Unit) {
  configure<MyExtension>(configure)
}
s
Thanks for the detailed explanation Adam! Yes I went with the option of providing the DSL after Björn’s helpful answer though i did not go the sneaky route 😄
a
my pleasure! Gradle’s monstrously confusing so it’s helpful to collaborate