How to use extension properties from custom plugin to set Android app version

Hi! I’m creating an Android plugin and got stuck at the following:

I want to set some properties from a DSL, and use these properties to set the Android version on my app.

Following some tutorials, I was able to make the DSL and the setting Android version part work independently, but I can’t combine them.

From my understanding, I can only access my DSL properties after evaluation (since they work if I access them inside a task, but they are null if I try to use them to set the version directly, at configuration time).

The common solution I found online is to access these properties inside an afterEvaluate block (and it works), but if I try setting the Android app version inside it I receive this error:

“It is too late to set versionCode. It has already been read to configure this project. Consider either moving this call to be during evaluation, or using the variant API.”

The Android version is set inside of the defaultConfig in the ApplicationExtension:

            extensions.configure<ApplicationExtension> {
                defaultConfig {
                    versionCode = extension.customCode.get()
                    versionName = extension.customName.get()
                }
            }

From my understanding, the defaulConfig versionCode and versionName are extension properties as well, so I believe I should be able to access my own extension when setting them. Is there a way I can set these properties at a lifecycle where I can both access my own properties and not receive the “It is too late” error?

If not, is there a good resource where I can understand more about the lifecycle of each step of the Gradle configuration?

Any help is appreciated!

The common solution I found online is to access these properties inside an afterEvaluate block

Don’t trust the internet.
Especially when you read advices for Gradle.
Gradle is relatively fast evolving and some bad practices that were recommendations long ago keep being recommended or used.
Always consider how old an advice is, or for which Gradle version.

Using afterEvaluate is almost always the wrong thing to do. In 98.7 % of the cases it is just symptom treatment like calling SwingUtilities.invokeLater or Platform.runLater to “fix” a GUI problem. It usually just shifts the problem to a later time where it is much harder to reproduce, much harder to debug, and much harder to fix. The main thing afterEvaluate does is introducing timing problems, ordering problems, and race conditions. That’s why the Property / Provider APIs were introduced, so that properties can lazily be wired together without the need to do delayed evaluation to give someone the chance to configure the value before you read it. What happens for example if someone sets the value in an afterEvaluate that is evaluated after your afterEvaluate? …

The problem is, that versionCode and versionName seem not to be Property so cannot be wired.

If you have settings in your extension that you need to access at configuration time like here and cannot just evaluate at execution time, then better not have them being properties in your extensions, but functions. The downstream project can then call that function instead of setting a property and your function implementation can then immediately use that value without the need to delay any execution.