Hello everyone, I try to create a plugin to set u...
# community-support
v
Hello everyone, I try to create a plugin to set up flavors. Of course I need to set some parameters and name of the flavor. I decided to use
register
method.
Copy code
flavorsExtension.config {
    register("flavorA") {
        variable = "variable1"
        applicationId = "com.flavor.a"
    },
    register("flavorB") {
        variable1 = "variable2"
        applicationId = "com.flavor.b"
    }
}
My plugin uses
NamedDomainObjectContainer
to get registered flavors when they are ready.
Copy code
interface FlavorsExtension {
    val config: NamedDomainObjectContainer<FlavorsConfig>
}

abstract class FlavorConfig(
    private val name: String,
) : Named {
    var variable1: String = "variable0"
    var applicationId: String? = null

    override fun getName(): String = name
}
And my plugin in the end looks like that:
Copy code
class SetupFlavorsPlugin : Plugin<Project> {
    override fun apply(target: Project) = with(target) {
        val flavorsExtension = extensions.create("flavorsExtension", FlavorsExtension::class.java)
        flavorsExtension.config.all { config ->
            setupFlavor(config)
        }
    }

    private fun Project.setupFlavor(config: FlavorConfig) {
        val androidExtension = project.extensions.getByType(ApplicationExtension::class.java)
        androidExtension.flavorDimensions("flavor1")
        androidExtension.productFlavors.register(config.name) { flavor ->
            flavor.dimension = "flavor1"
            val appId = getApplicationId(config)
            println("SetupFlavorsPlugin: $appId ${config.applicationId} ${config.marketing} ${config.ticketing}")
            if (appId != null) {
                flavor.applicationId = appId
                flavor.resValue("string", "application_id", appId)
            }
        }
    }
}
The issue is that
name
is there when
.all { }
is called, but not parameters.
afterEvaluate { }
helps to get registered flavors with variables but it's too late for me to set up flavors in android extension. The only thing that works for me is to use
add { }
instead of
register { }
Copy code
flavorsExtension.config {    
     add(
        FlavorConfig(
            name = "flavorA",
            variable = "variable1"
            applicationId = "com.flavor.a",
        )
    )
Do you know at what time in configuration phase these variables are set up? Thanks in advance 🙂
v
That's the same problem with all domain containers. If you call
create
or
register
, the last parameter is the configure action that in your case sets variable and applicationid. The problem is, that the configuration actions are run in the order they are added. So your plugin adds its configuration action (that you registered with
all
or
configureEach
) first, then the consumer does the
register
call with another configuration action that comes second. Because of that the values for the properties are not set yet when the action in the plugin is executed. If the place where you want to use those property values accepts `Provider`s and properly treats them lazily, you could make your properties `Property`s and wire them to where they are needed. If you need those values at configuration time to create some other objects (which probably is the case for product flavors, I'm not into Android development), then this would not help or work. In that case this is the same problem that you always have when you register some extension and need to change other non-
Provider
configuration from the plugin based on those properties. Using
afterEvaluate
is one band-aid "solution", but one that should be avoided as hell, besides that you said it does not work in your case anyway. The main effect of
afterEvaluate
is to introduce ordering problems, timing problems, and race conditions, and it should imho be avoided at almost any cost. The solution I recommend in such cases is to instead use functions. In you case your
FlavorsExtension
could for example instead have some
createFlavor(name, variable, applicationId)
function that then does the according configuration as it has all the necessary variables available as arguments right away. If you for some reason nevertheless need a named domain object collection, that method could also do the
register
call to fill it.
v
Thanks a lot!
It is helpful 👍
To create a method is also an interesting solution here. I'll try that
👌 1
Maybe even the only one 😅
What I ended up with after our discussion:
Copy code
class SetupFlavorsPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        val androidExtension = target.extensions.getByType(ApplicationExtension::class.java)
        target.extensions.create(
            "flavorsExtension",
            FlavorsExtension::class.java,
            target,
            androidExtension,
        )
    }
}

abstract class FlavorsExtension(
    private val project: Project,
    private val androidExtension: ApplicationExtension,
) {
    fun createFlavor(
        name: String,
        variable: String,
        applicationId: String? = null,
    ) = with(project) {
        val config = FlavorConfig(name, variable, applicationId)
        setupProductFlavor(androidExtension, config)
    }
}

fun Project.setupProductFlavor(...
In my root build.gradle.kts 👇
Copy code
flavorsExtension {
    createFlavor(
        name = "test",
        variable = "variable1,
        applicationId = "com.flavor.a"
    )
....
}
I guess, I got it right what you meant above 🙂 Thanks!
v
Besides that it will not compile, yes. :-D
v
Except that I decided to remove
androidExtension
from
FlavorsExtension
, but it compiled and built the app with the flavor that was added 🤔 Am I missing something?
v
You define
createFlavor
but call
createPartner
:-D
👍 1
v
Oh 😅